const { Button, Divider } = window.VoxAwenDesignSystem_e48818;

/* The two Cal.com events this page books. Both namespaces are initialised in
   index.html — an event type with no namespace there opens an empty modal.
   The attributes go on a wrapper rather than the button itself: the design
   system's Button forwards only its own props, and Cal resolves the link with
   closest(), so a span around it works. */
const CAL_SESSION = { "data-cal-link": "esther-wilson-onsbsr/graceful-ignited-bodies", "data-cal-namespace": "graceful-ignited-bodies", "data-cal-config": '{"layout":"month_view"}' };
const CAL_CALL    = { "data-cal-link": "esther-wilson-onsbsr/15min", "data-cal-namespace": "15min", "data-cal-config": '{"layout":"month_view"}' };

const h2Style = { font: "var(--text-h2)", color: "var(--text-display)", margin: 0 };
const h3Style = { font: "var(--text-h3)", color: "var(--text-display)", margin: 0 };

const GIB = "public/gib/";

// In-page contents, offered from the nav while this page is showing.
const gibSections = [
  { id: "gib-expect",     label: "What to expect",     flush: true, target: chapterScrollTarget },
  { id: "gib-who",        label: "Who is this for" },
  { id: "gib-investment", label: "Investment",         flush: true },
  { id: "gib-bespoke",    label: "Group sessions", flush: true },
  { id: "testimonials",   label: "Testimonials" },
  { id: "gib-faq",        label: "FAQ" },
];

// The ritual, told as scenes. Each is one held photograph, pinned full-screen —
// a wide shot of an action, which is what makes a stage a stage. The head being
// wrapped used to sit here too; it is a close-up of one gesture rather than a
// stage of the ceremony, so it closes the page instead. See .gib-plate below.
const scenes = [
  {
    key: "arrival",
    label: "Sitting together",
    line: "Over tea, while you enjoy a scented foot bath, you'll be invited to share your intention for the session.",
    image: GIB + "arrival.jpg",
    alt: "A foot washing prepared in a singing bowl at the threshold of the room",
    /* Her head starts 5% down this frame. Anchored to the top; what goes at the
       foot is floorboards. The x holds her and the bowl together on a phone.
       (The seated client at the right is cut off in the original photograph, so
       no crop recovers her.) */
    pos: "55% 0%",
  },
  {
    key: "rocking",
    label: "Rocking with scarves",
    line: "Lying down, your body will be rocked — supporting the movement of joints, muscles, tissues and fluids.",
    image: GIB + "rocking.jpg",
    alt: "The chest rocked gently with a woven scarf",
    /* Her head starts 7% down. Anchored to the top, which costs the blanket
       fringe at the foot and leaves the wrapped head — 73% to 84% down — intact.
       The x is left of centre so a phone keeps that wrapped head in beside her. */
    pos: "42% 0%",
  },
  {
    key: "massage",
    label: "Abdominal & chest massage",
    line: "Tender touch will release muscular tension, balance hormones, boost circulation and immunity — easing you into deep restoration.",
    image: GIB + "touch.jpg",
    alt: "Oil massage across the belly during the session",
  },
  {
    key: "wrapping",
    label: "Wrapping & closing the body",
    line: "Physical compression will contain and ground your body — signalling safety to your nervous system and releasing tension.",
    image: GIB + "wrapping.jpg",
    alt: "A client wrapped in coloured rebozos indoors, being settled by hand",
    /* Esther's head starts 3% down this frame, so a centred crop takes the top
       of it. Anchored to the top instead: what a wide screen loses is the mat
       and the fringe at the foot. The x only comes into play on a phone, where
       the frame is cropped sideways rather than vertically — nudged right of
       centre so the pink wrap stays in beside her. */
    pos: "55% 0%",
  },
  {
    key: "sound",
    label: "Intuitive singing & drumming",
    line: "Wrapped and resting, you will be drummed and sung over — allowing you to listen inwardly.",
    image: GIB + "sound.jpg",
    alt: "Live drumming beside a wrapped client",
    /* Her head is 7% down and the drum begins at 9%, so a centred crop clips
       both. Anchored to the top, and well right of centre so the phone crop
       holds the drum as well as her — it is the whole point of the frame. */
    pos: "72% 0%",
  },
];

// Kept with the shelved collage: four portraits spanning the ritual.
// Flanking slots settle tall and narrow, so these want to be portrait.
const collageSides = [
  { src: GIB + "touch-a.jpg", alt: "Warm oil massage across the belly and chest" },   // left, top
  { src: GIB + "rocking-a.jpg", alt: "The waist bound and rocked in a woven scarf" }, // left, bottom
  { src: GIB + "sound-a.jpg", alt: "A frame drum played at the client's side" },      // right, top
  { src: GIB + "arrival-a.jpg", alt: "Feet washed in a bronze singing bowl" },        // right, bottom
];

// The gallery. Captions are Esther's own names for each frame.
const stripCards = [
  { src: GIB + "arrival-a.jpg",  alt: "Feet washed in a bronze singing bowl",              caption: "Footbath" },
  { src: GIB + "inward-b.jpg",   alt: "A face at rest, eyes covered by a violet wrap",     caption: "Head wrap" },
  { src: GIB + "rocking-a.jpg",  alt: "The waist bound and rocked in a woven scarf",       caption: "Hip rock" },
  { src: GIB + "rocking-b.jpg",  alt: "A patterned scarf drawn along the legs on the grass", caption: "Foot rock" },
  { src: GIB + "touch-a.jpg",    alt: "Warm oil massage across the belly and chest",       caption: "Chest massage" },
  { src: GIB + "wrapping-c.jpg", alt: "A scarf drawn around the hips out on the grass",    caption: "Hip wrap" },
  { src: GIB + "wrapping-a.jpg", alt: "Hands drawing a magenta wrap tight",                caption: "Chest wrap" },
  { src: GIB + "wrapping-b.jpg", alt: "The feet bound together in a patterned scarf",      caption: "Foot wrap" },
  { src: GIB + "sound-a.jpg",    alt: "A frame drum played at the client's side",          caption: "Drumming" },
];

const sessionOptions = [
  {
    format: "Individual session",
    length: "2.5–3 hours",
    price: "£170",
    /* Set directly under the price and at display size, so the reason for the
       number is read with it rather than in the small print below. */
    offer: "Early adopter rate — usually £210",
    note: "Limited availability — first 10 clients only",
  },
];

const testimonials = [
  { name: "Tara", pull: "Her singing is truly transcendental.", paras: [
    "I am so blessed to have encountered Esther's offerings early in my journey towards motherhood. Esther creates the most beautiful atmosphere during her sessions. I felt transported to another reality, one that was full of love, tenderness, care, and joy! I cannot recommend this experience highly enough to any woman who wants to connect more deeply with her body and inner world.",
  ] },
  { name: "Anna", pull: "I felt so safe and secure. I felt so loved.", paras: [
    "It's such a wonderful experience to be given the luxury of time and space to relax, in a period where so much is demanded of you, and to be made to feel truly special, when so often after having a baby all the focus goes to the baby.",
    "After pregnancy, your body changes so much. This helped bring awareness back to the edges of my body, to feel where I begin and end again.",
  ] },
  { name: "Rosie", pull: "It was inspiring and strange and wonderful.", paras: [
    "As a mother postpartum I was struggling with feeling a lack of autonomy over my own body, and it felt amazing to have time spent listening to my body and the things it has been lacking over the last months, caring physical touch, movement, sensory experiences. The massage, wrapping and meditations definitely unlocked something, enabling me to move into my deeper self rather than be only focused on the surface survival mechanisms early motherhood lands you in.",
    "Her singing is almost spiritual and adds to the trance-like sensations you are placed in after careful massage and womb-like wrapping. If you are moving through a transition in your life and need to take a beat for yourself, go and experience it for yourself.",
  ] },
  { name: "Anita", pull: "A moment of pure surrender.", paras: [
    "Esther took me on a deep journey inwards. I received so much gentleness, joy, and love from Esther, it allowed my body to fully let go... it's hard to put into words how deeply this 'holding' has supported me through a difficult time of change.",
  ] },
  { name: "Ewa", pull: "With my entire body wrapped, I felt I was in a cocoon supporting stillness and transformation.", paras: [
    "The massage was outstanding. Esther's touch was confident and gentle, pressure perfect without needing any adjustment. The massage around my hip bones was exceptional, giving so much attention to a part of the body I rely on every day for mobility, dance, and self-expression, but rarely think about.",
    "During the wrapping, having my head wrapped and swinging gently from side to side was a new kind of soothing.",
  ] },
  { name: "Sophie", pull: "I felt thoroughly cared for and honoured.", paras: [
    "Esther clearly thought deeply about how to prepare the ritual. I was welcomed with some rose tea, and a foot wash prepared inside a singing bowl which was a beautiful touch. She started the treatment with washing my feet which was a new and lovely experience for me. At the end of the wrapping she sung, and she has a beautiful voice which added to the experience.",
  ] },
];

/* One flat list: the four group headings — Before your session, During your
   session, Fears and comfort, After your session — have been dropped, and the
   questions run in one sequence. An answer is an array where it runs to more
   than one paragraph. */
const faqItems = [
  { q: "How do I book a session?",
    a: "You can book a session directly through the website using the booking link on this page. For personalised group sessions, or if you would like to have this at home, please reach out via the contact form or set up a free 15 minute discovery call." },
  { q: "Where does the session take place?",
    a: ["The session takes place on the floor, supported by cushions, blankets, and bolsters for your comfort.",
         "The session can be received either in my treatment space in central Cambridge or in the comfort of your own home if you are based closeby. For postpartum clients, home visits are often the preferred option, allowing you to be in a familiar and supportive environment during this tender period of rest. As travel and availability need to be arranged individually, in-home sessions are booked directly rather than through the online booking system. Please get in touch via the contact form to discuss availability."] },
  { q: "How long before a session should I arrive?",
    a: "Please arrive five minutes before the start of your session." },
  { q: "What should I wear?",
    a: "During the session you will be covered with scarves and blankets at all times to ensure you feel warm, comfortable, and secure throughout. Please dress to your own comfort, something comfortable and easy to lie down and move in — loose fitting trousers or shorts on the bottom half, and a top that can be removed for the massage is ideal." },
  { q: "What do I need to do before a session?",
    a: "You'll receive a short questionnaire on Google Forms a day ahead of your session. Please complete that so I can understand a bit about you and hold the space appropriately." },
  { q: "What do I need to do in a session? Will I be awake or asleep?",
    a: "The session is a rare opportunity to be radically honest with yourself. What is your body asking of you? What's stuck inside you that's ready to move? I hold a space of acceptance, and you guide how much you share and how deep you want to go. I respond intuitively to your signals, both verbal and bodily. It's quite common to drift in and out of sleep, especially once you're wrapped and the drumming begins. If you do, you won't be rushed or woken abruptly. I'll bring you back gently towards the end, with time to reorient before we move into the closing conversation." },
  { q: "What does a session feel like?",
    a: "Every woman experiences it differently. Many describe a deep, gradual release of tension, a sense of being held and contained by the wrapping, and a feeling of safety that lets the mind soften and the body finally let go. The drumming and voice often carry people into a very inward, sometimes vivid, state, some describe images or memories surfacing; others simply rest more deeply than they have in a long time." },
  { q: "What benefits are associated with a session like this?",
    a: "This work supports deep relaxation and nervous system regulation, a felt sense of being witnessed and held, and space to process or mark a significant transition in a way that doesn't rely on words alone. It isn't a clinical treatment, and therefore does not aim to be a substitute for therapy or medical care." },
  { q: "How often do people come for sessions like this?",
    a: "For some, one session is enough. For others going through a bigger transition, a cycle of sessions can help you go deeper — if that's what you're looking for, get in touch and we can shape something together." },
  { q: "Does it matter if I get claustrophobic easily?",
    a: "I'll be communicating with you throughout, and you'll guide me on the pressure, pace, and rhythm of the wrapping. Where and how tightly you're wrapped is led by you, and at any time you're free to be unwrapped. We can also agree on a signal beforehand, via the intake form or on the day, so you always have a way to tell me if you need more space." },
  { q: "Will there be any discomfort or pain during a session?",
    a: "The session aims to be relaxing and restorative. I'll be tuning into you throughout and adjusting pressure, pace, and rhythm to what feels right. Sometimes emotions or physical sensations surface as something ready to be released; this is a normal part of the process, and you're always free to pause or ask for something different." },
  { q: "Can my baby stay with me during the session?",
    a: ["Yes, within the fourth trimester, your baby will have a need to be close to you.",
         "It is usual to feed the little one before the session starts, and they can enjoy the relaxing vibes whilst they (hopefully!) sleep near you.",
         "If you would like to have someone, perhaps your partner, there to take care of your baby whilst you receive the massage that's completely fine too - whatever helps you to be able to relax and take this time for yourself."] },
  { q: "If I'm on my cycle, will this affect the session?",
    a: "This treatment can be supportive if you're on your cycle, particularly the abdominal work, which can aid blood flow and ease hip discomfort. I'll tune into your body directly and often go slower and gentler if that's what your cycle is asking for. If you'd rather reschedule, that's no problem at all." },
  { q: "If I'm pregnant, will this affect the session?",
    a: "Yes — currently I do not offer this session for pregnant women. Please reach out if you are looking for help, I may be able to put you in touch with someone else who can." },
  { q: "What happens once the session ends?",
    a: "You're unwrapped slowly, with your eyes uncovered last, and given time to arrive back gently before we move into a short integration conversation. It's normal for things to keep settling in the days that follow; if anything comes up you'd like to talk through, you're welcome to message me." },
];

/*
  The ritual on one pinned stage. Every stage is a layer; scrolling cross-dissolves
  between them rather than sliding the next up over the last, so there is never a
  hard edge travelling across the picture. Each frame also drifts very slightly
  larger while it is held, so nothing sits frozen.
*/
function StoryStage({ scenes, depth }) {
  const ref = useBlockProgress("--sp");
  return (
    <div className="gib-unit gib-stage" style={{ zIndex: depth, "--n": scenes.length }} ref={ref}>
      <div className="gib-stage-pin">
        {scenes.map((scene, i) => (
          <div key={scene.key} className={"gib-layer"} style={{ "--i": i }}>
            <img src={scene.image} alt={scene.alt} loading={i === 0 ? undefined : "lazy"}
              style={scene.pos ? { objectPosition: scene.pos } : undefined} />
            <div className="gib-scene-scrim" />
            <div className="gib-scene-copy">
              <div className="gib-scene-inner">
                <Eyebrow onDark>{scene.label}</Eyebrow>
                <p className="gib-scene-line">{scene.line}</p>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/*
  SHELVED: the five-image closing collage. NOT CURRENTLY RENDERED — the gallery
  below closes the sequence instead. Kept whole so it can come back: render it
  inside .gib-story at depth 2, above <SceneStrip>, with

    <SceneCollage
      depth={2}
      centre={{ src: GIB + "final.jpg", alt: "..." }}
      sides={collageSides}
    />

  Its styles are still in story.css under .gib-collage.

  The last frame draws back and the details gather in around it, gathering the
  whole ritual into one image.
  Flanking slots settle tall and narrow, so they want portrait images.
  The move finishes at 60% so the section below has room to rise over the top.
*/
function SceneCollage({ centre, sides, depth }) {
  const ref = useBlockProgress("--cp", 0.6);
  return (
    <div className="gib-unit gib-collage" style={{ zIndex: depth }} ref={ref}>
      <div className="gib-collage-stage">
        <div className="gib-collage-col">
          {sides.slice(0, 2).map((s) => (
            <figure key={s.src} className="gib-collage-side"><img src={s.src} alt={s.alt} loading="lazy" /></figure>
          ))}
        </div>

        <div className="gib-collage-main">
          <img src={centre.src} alt={centre.alt} loading="lazy" />
          <div className="gib-scene-scrim" />
        </div>

        <div className="gib-collage-col">
          {sides.slice(2, 4).map((s) => (
            <figure key={s.src} className="gib-collage-side"><img src={s.src} alt={s.alt} loading="lazy" /></figure>
          ))}
        </div>

      </div>
    </div>
  );
}

/*
  The gallery: a rolling filmstrip. The row travels horizontally as the reader scrolls
  down, so every photograph is shown whole at a consistent size rather than
  cropped into a narrow slot. Each caption travels with its own card.
  translateX uses 100vw - 100% — 100% being the track's own width — so the last
  card lands flush right whatever the card count.
*/
function SceneStrip({ cards, title, depth }) {
  const ref = useBlockProgress("--gp");
  return (
    <div className="gib-unit gib-strip" style={{ zIndex: depth }} ref={ref}>
      <div className="gib-strip-pin">
        {title ? <h2 className="gib-strip-head">{title}</h2> : null}
        <div className="gib-strip-track">
          {cards.map((c) => (
            <figure key={c.src} className={"gib-strip-card" + (c.wide ? " gib-strip-card--wide" : "")}>
              <img src={c.src} alt={c.alt} loading="lazy" />
              <figcaption>{c.caption}</figcaption>
            </figure>
          ))}
        </div>
      </div>
    </div>
  );
}

/* A filled cream button for the green band.

   The design system has no solid on-dark variant, and its bundle is generated
   from sources that are not in this repo — a variant added there would be
   discarded silently by any later rebuild, falling back to the red primary. So
   the fill is passed as a style, and the hover the component would have given
   us is held here instead. Ink on cream measures 14.9:1 against the band's
   3.1:1 for ember. */
function SolidOnDark({ children, onClick, cal }) {
  const [hover, setHover] = React.useState(false);
  return (
    <span style={{ display: "inline-flex" }} {...(cal || {})}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
      <Button variant="onDark" onClick={onClick} style={{
        background: hover ? "var(--sand-1)" : "var(--text-on-dark)",
        color: "var(--ink-0)",
        borderColor: "transparent",
      }}>
        {children}
      </Button>
    </span>
  );
}

function Voice({ t }) {
  return (
    <div className="gib-voice">
      <div>
        <blockquote className="gib-voice-pull">{"\u201C" + t.pull + "\u201D"}</blockquote>
        <span className="gib-voice-name">{"\u2014 " + t.name}</span>
      </div>
      <div className="gib-voice-body">
        {t.paras.map((p, i) => <p key={i}>{p}</p>)}
      </div>
    </div>
  );
}

/* Classed rather than styled inline, so the band can set its own colours — see
   .gib-faq in story.css, where this sits on the page's green. */
function FaqItem({ q, a }) {
  return (
    <details className="gib-faq-item">
      <summary>
        {q}
        <span aria-hidden="true">+</span>
      </summary>
      {(Array.isArray(a) ? a : [a]).map((para) => <p key={para}>{para}</p>)}
    </details>
  );
}

/* A single tab pinned to the edge of the screen, in the place Companion Voices
   keeps its own pair. It opens the Cal.com overlay for the session itself
   directly, rather than routing through the contact form or the fifteen-minute
   call the Contact page books. Like that dock, it waits for the chapter plate to finish so it
   is not arriving over the two words as they land. */
function BookDock() {
  const shown = useAfterChapter();
  return (
    <div className={"dock" + (shown ? " dock--in" : "")} aria-hidden={shown ? undefined : "true"}>
      <div className="dock-tabs">
        <button
          className="dock-tab dock-tab--accent"
          type="button"
          data-cal-link="esther-wilson-onsbsr/graceful-ignited-bodies"
          data-cal-namespace="graceful-ignited-bodies"
          data-cal-config='{"layout":"month_view"}'
        >
          Book here
        </button>
      </div>
    </div>
  );
}

function Offerings({ onNav }) {
  const customRef = useEntryProgress("--kp");
  return (
    <section id="offerings">

      {/* Hero — same pinned reveal as the homepage */}
      <ScrollReveal
        image={GIB + "hero.jpg"}
        alt="Esther leaning over a wrapped client during a session in a meadow at dusk"
        mark={["Graceful", "Ignited", "Bodies"]}
        layout="left"
        /* Held, not resolving into a closing frame. Witness & nourish and its
           supporting line used to sit here; the page opened with the photograph
           collapsing into them and then the chapter plate taking over, which was
           two transitions before the ritual had begun. The plate does it alone
           now. The words are kept in the copy document under gib.hero.* in case
           they belong somewhere else. */
        still
      />

      <div className="rise rise--compact">

      {/* Names the chapter before the ritual begins */}
      <ChapterPlate
        id="gib-expect"
        image={GIB + "chapter.jpg"}
        alt="The session laid out on blankets in a meadow, seen from across the grass"
        left="What"
        right="to expect"
      />

      {/* Contents, offered once the photographic sequence is behind you */}
      <SectionStrip sections={gibSections} startId="gib-who" />

      {/* The ritual, in scenes */}
      <div className="gib-story">
        <StoryStage scenes={scenes} depth={1} />

        {/* The gallery closes the sequence. The five-image collage that used to
            sit between them is shelved — see SceneCollage above to bring it
            back, at depth 2. */}
        <SceneStrip cards={stripCards} title="A closer look" depth={3} />
      </div>

      {/* Rises up over the pinned collage, the way the homepage opens into About */}
      {/* No bottom padding: the FAQ is the last thing in here and it is a
          full-bleed band with its own. Left in, it put 112px of beige between the
          FAQ's green and the closing plate's. */}
      <div className="gib-outro" style={{ padding: "112px 48px 0" }}>
        <div style={{ maxWidth: 1120, margin: "0 auto" }}>

          {/* Thresholds — a centred statement */}
          <div className="gib-statement" id="gib-who">
            <p>
              {"This session can meet you at many "}<em>thresholds</em>{" — from motherhood and menopause, to seasons of grief, heartbreak, or simply holding too much for too long."}
            </p>
            <p>
              {"It offers deep "}<em>witness and nourishment</em>{", helping you close one chapter and "}<em>ignite</em>{" the next."}
            </p>
          </div>

          {/* Session options — a dark band, with the price carrying the weight */}
          <div className="gib-rates" id="gib-investment">
            <div className="gib-rates-inner">
              <div className="gib-rates-grid">
                {sessionOptions.map((o) => (
                  <div key={o.format} className="gib-rate">
                    <span className="gib-rate-name">{o.format}</span>
                    <p className="gib-rate-price">{o.price}</p>
                    <p className="gib-rate-offer">{o.offer}</p>
                    <p className="gib-rate-offer-note">{o.note}</p>
                    <p className="gib-rate-length">{o.length}</p>
                  </div>
                ))}
              </div>

              {/* Above the rule: the rule belongs to the skills-swap note, and
                  the main action should not sit below a footnote. */}
              {/* Filled cream, not the red primary: ember measures 3.1:1 against
                  this band's green, so the button sank into its own ground and
                  only the hue differed — which is what read as a clash. */}
              <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginTop: 30 }}>
                <SolidOnDark cal={CAL_SESSION}>Book a session</SolidOnDark>
              </div>

              <div className="gib-rates-foot">
                <h3 className="gib-rates-foot-title">Community offering</h3>
                <p className="gib-rates-aside">
                  A limited number of sessions each month are offered on a sliding scale price in exchange for your own professional skills — please ask.
                </p>
              </div>
            </div>
          </div>

          {/* Customised experiences — full-bleed frame after the prices. */}
          <section className="gib-custom" id="gib-bespoke" ref={customRef}>
            <img src={GIB + "bowls.jpg"} alt="Himalayan singing bowls arranged around a wrapped body" />
            <div className="gib-custom-scrim" />
            <div className="gib-custom-copy">
              <div className="gib-custom-inner">
                <Eyebrow onDark>By arrangement</Eyebrow>
                <h2 className="gib-custom-title">Personalised group session</h2>
                <p className="gib-custom-line">
                  A session curated with your friends and family — a chance to share blessings, take part in the ceremony together, and offer their love and support as you move through this transition.
                </p>
                <p className="gib-custom-line">
                  The session includes massage, wrapping, song, and words of support, with space to feast together afterwards.
                </p>
                {/* The group size, given the same small-caps treatment the
                    session length has on the rate card above. */}
                <p className="gib-custom-meta">4–8 people</p>

                {/* The fifteen-minute call, not the session: a group booking is
                    arranged rather than picked off a calendar. */}
                <span style={{ display: "inline-flex" }} {...CAL_CALL}>
                  <Button variant="onDark">Start a conversation</Button>
                </span>
              </div>
            </div>
          </section>

          {/* Testimonials — stacked. No card, no rotation: the line that carries
              each account is set in display type, the full text stays beside it. */}
          <div id="testimonials" style={{ marginBottom: 96, scrollMarginTop: 96 }}>
            <Eyebrow>Testimonials</Eyebrow>
            <h2 style={{ ...h2Style, margin: "12px 0 56px", textTransform: "uppercase", letterSpacing: ".03em" }}>Voices</h2>
            <div className="gib-voices">
              {testimonials.slice(0, 3).map((t) => <Voice t={t} key={t.name} />)}
            </div>

            {/* The other three roll up, as the FAQ answers do. Six accounts at
                this length is most of a screen each, and the page has a good
                deal after them. */}
            <details className="gib-voices-more">
              <summary>Read more voices</summary>
              <div className="gib-voices">
                {testimonials.slice(3).map((t) => <Voice t={t} key={t.name} />)}
              </div>
            </details>
          </div>

          {/* FAQ — a full-bleed band on the page's green, like the rates above */}
          <div className="gib-faq" id="gib-faq">
            <div className="gib-faq-inner">
              <h2 className="gib-faq-title">FAQ</h2>
              <div className="gib-faq-groups">
                {faqItems.map((item) => <FaqItem key={item.q} q={item.q} a={item.a} />)}
              </div>
            </div>
          </div>

        </div>
      </div>

      {/* Closing plate. The page otherwise ends on type — Voices, FAQ, footer —
          with nothing held. One frame, one line, and out. */}
      <section className="gib-plate" aria-label="Closing image">
        <img src={GIB + "inward.jpg"} alt="A rebozo scarf drawn across the eyes, a hand steadying it" loading="lazy" />
        <div className="gib-plate-scrim" />
        <div className="gib-plate-copy">
          <p>Sight is covered, and the attention turns inward.</p>
        </div>
      </section>

      </div>

      <BookDock />
    </section>
  );
}

window.pageSections = Object.assign(window.pageSections || {}, { offerings: gibSections });
Object.assign(window, { Offerings, gibSections });
