Skip to content

Instantly share code, notes, and snippets.

@0xdevalias
Last active August 24, 2026 17:39
Show Gist options
  • Select an option

  • Save 0xdevalias/468e861ed30ea16c610109eaaf3ac735 to your computer and use it in GitHub Desktop.

Select an option

Save 0xdevalias/468e861ed30ea16c610109eaaf3ac735 to your computer and use it in GitHub Desktop.
Some notes and a browser-console snippet for expanding BlossomUp report content into a print-friendly layout for saving as PDF.

BlossomUp: Print / Save Reports as PDFs with All Content Expanded

Warning

Treat BlossomUp and its reports with caution. The five report types inspected for this project showed signs of weak editorial control: obvious copy errors, repeated or misplaced text, large amounts of generic type-reference material, limited answer-level personalisation, and clinical-sounding labels without disclosed validation of BlossomUp's questionnaires or scoring. BlossomUp's own Terms of Service say the quizzes are for entertainment and general information, are not psychological or psychiatric assessments, and should not be treated as comprehensive or accurate.

Public reputation signals are also concerning and unusually inconsistent. As checked on 2026-08-25, ProductReview showed 1.1/5 from 577 reviews, with recurring complaints about the post-quiz paywall, subscriptions, cancellation, and generic reports. The BBB profile for Transcend Labs LLC, which lists BlossomUp as an alternate name, had an F rating, 33 complaints, and cited failure to respond to 26 complaints. By contrast, Trustpilot showed 4.0/5 from about 18,300 reviews; its page says the company has a claimed profile, pays for a Trustpilot subscription, invites reviews, and has a merged profile. That contrast does not itself prove review manipulation, and user complaints are allegations rather than adjudicated findings, but it is a strong reason to read the checkout terms carefully, retain cancellation confirmation, and avoid treating the reports as authoritative.

I did not find a court or regulator decision establishing that BlossomUp is fraudulent, or a substantiated public plagiarism investigation. The more supportable quality assessment from the inspected material is heavily templated, poorly edited, and frequently derivative, not proof that it was plagiarised or AI-generated.

BlossomUp is a personal-growth quiz and personality-test site offering interactive result reports across relationship, personality, and mental-health-adjacent topics.

These browser-console snippets convert supported BlossomUp results into static, print-friendly layouts so tabs, carousels, charts, and long report sections can be reviewed together and saved as more useful PDFs. This is an archival and formatting utility, not an endorsement of the quizzes' quality or validity.

Table of Contents

Overview

The normal reports use tabs, carousels, responsive variants, and collapsible sections that work interactively but can hide or awkwardly split content when printed. Each snippet temporarily reshapes one supported report type for reading and PDF export without changing anything in the BlossomUp account or server-side report.

Subscription / cancellation warning

Caution

In the Australian purchase flow observed on 2026-08-24, the quiz initially appeared free, then presented a 15-minute offer of about A$2.95 to unlock the completed result. That purchase also began a 7-day trial which would convert to a recurring subscription of roughly A$45/month unless cancelled. The cancellation flow then presented a lower-priced retention offer of around A$10/month; continue through every cancellation step if the intention is to cancel, and keep the final confirmation.

The observed account retained report access for the remainder of the trial, although BlossomUp's published policy currently says access during discounted trials may end immediately upon cancellation.

Offers, currencies, prices, and access behaviour can vary, so check the exact checkout terms and the current Subscription & Cancellation Policy and Terms of Service. The timed post-quiz reveal, auto-renewing trial, and cancellation-stage retention offer are worth recognising as a dark-pattern-style marketing funnel.

Interpretation warning

Treat these reports as entertainment or prompts for self-reflection, not as validated psychological assessments. In particular, a quiz labelled “BPD” or “Narcissist” cannot diagnose a mental-health condition; discuss real concerns with a qualified clinician.

What it changes

  • Expands interactive or hidden report content into a static, print-friendly layout.
  • Simplifies surrounding page chrome and adds report-specific print layout rules.
  • Adds a contents section where useful and sets a descriptive page title for the suggested PDF filename.

Supported reports and testing status

The snippets were developed on macOS against one authenticated example of each listed report type. All five pass a JavaScript syntax check and fail safely if the visible report title does not match the intended report.

Report Result included in suggested filename Validation status
8 Expressions of Love Primary and Secondary Love Styles Iteratively reviewed in Chrome browser view and print preview
Sexual Values Report Sexual Persona Developed against one live report; latest print revision needs re-checking
Conflict Types Report Conflict Type Developed against one live report; final print output not manually confirmed
BPD Report Detected result type(s) Developed against one live report; final print output not manually confirmed
Narcissist Report Primary and Secondary styles Developed against one live report; final print output not manually confirmed

Only 8 Expressions of Love received repeated end-to-end review in both the normal Chrome view and Chrome print preview. The other snippets were built from their live page structures but should be checked carefully in browser and print preview before relying on the resulting PDF. Safari remains unverified.

How to Use

Only paste browser-console code that you have read and trust. Code run in developer tools has access to the currently open page.

Open the relevant BlossomUp result and wait for the full report to finish loading before following the browser-specific steps below.

Choose the matching snippet

Copy the complete snippet whose heading exactly matches the title displayed on the report page. These are deliberately standalone, report-specific snippets rather than one large generic script. If the title does not match, the snippet logs a warning and changes nothing.

Chrome

  1. Open the report in Chrome.
  2. Open the JavaScript Console with Command + Option + J on macOS or Control + Shift + J on Windows/Linux. You can also use Chrome menu → More Tools → Developer Tools, then select Console.
  3. Copy the complete matching snippet below, paste it into the Console, and press Enter.
  4. If Chrome displays its self-XSS paste warning, review the warning and follow its on-screen instruction only after confirming that you trust the snippet.
  5. Confirm that the expected tabs, cards, carousels, and long-form sections are all visible.

See Open Chrome DevTools for Google's current instructions.

Safari

  1. If the Develop menu is missing, open Safari → Settings → Advanced and enable Show features for web developers.
  2. Open the BlossomUp report, then choose Develop → Show Web Inspector or press Command + Option + I.
  3. Open the Console tab or console prompt.
  4. Copy the complete matching snippet below, paste it into the Console, and press Return.
  5. Confirm that the expected tabs, cards, carousels, and long-form sections are all visible.

See Use the developer tools in the Develop menu in Safari on Mac and Safari Web Inspector for Apple's current instructions.

Print or save as PDF

  1. Close any print dialog that was already open, then reopen it after applying the snippet so the layout and suggested filename refresh.
  2. Open the print dialog with Command + P on macOS or Control + P on Windows/Linux.
  3. Compare Portrait and Landscape in the preview. For the extensively tested 8 Expressions of Love report, Portrait with default margins and normal scale produced the best final result; the scripts do not force an orientation.
  4. Enable Background graphics if the browser offers that option and you want the report's coloured cards and chart backgrounds preserved.
  5. Review the full preview before selecting Save as PDF.

The suggested filename is derived from the visible report title and result names rather than hardcoding a particular person's result. It follows this general form:

YYYYMMDD - BlossomUp - Report Title - Detected Result.pdf

The date is the browser's current local date when the snippet runs because the inspected pages did not expose their original test-completion dates.

Restore the interactive page

Reload the page. Because the snippets only change the current page in memory, a normal reload restores the site's original layout and title.

Report-specific Snippets

8 Expressions of Love

The most extensively tested layout, including charts, contents, long-form reference material, page breaks, and footer suppression.

(() => {
  const STYLE_ID = "blossomup-print-layout";

  // Avoid duplicating the generated contents and layout if pasted twice.
  // Reloading the page is the single, reliable way to restore the site.
  if (document.getElementById(STYLE_ID)) {
    console.info("BlossomUp print layout is already applied. Reload to restore the page.");
    return;
  }

  // Read the visible report title from the page, then fail safely if this
  // report-specific snippet is pasted into a different BlossomUp report.
  const reportHeading = [...document.querySelectorAll("#report h1, #report h2, #report h3")]
    .find((heading) => heading.textContent.trim());
  const reportTitle = reportHeading?.textContent.trim();
  if (reportTitle !== "8 Expressions of Love") {
    console.warn(
      `This snippet supports the 8 Expressions of Love report, but detected ${
        reportTitle ? `“${reportTitle}”` : "no report title"
      }. Nothing was changed.`,
    );
    return;
  }

  const commonAncestor = (elements) => {
    if (!elements.length) return null;
    for (let candidate = elements[0]; candidate; candidate = candidate.parentElement) {
      if (elements.every((element) => candidate.contains(element))) return candidate;
    }
    return null;
  };

  // Hide app navigation and floating UI, while leaving report content intact.
  document.querySelectorAll("body *").forEach((element) => {
    if (["fixed", "sticky"].includes(getComputedStyle(element).position)) {
      element.dataset.buPrintChrome = "";
    }
  });

  const hero = reportHeading?.parentElement?.parentElement;
  if (hero) hero.dataset.buPrintHero = "";

  // Expose both Primary and Secondary panels and replace the interactive tabs
  // with clear static labels.
  document.querySelectorAll('[data-controller~="slideshow"]').forEach((controller) => {
    const tabs = [...controller.querySelectorAll('[data-slideshow-target="style"]')];
    const tabRow = commonAncestor(tabs);
    if (tabRow && tabRow !== controller) tabRow.dataset.buPrintTabs = "";

    controller.querySelectorAll('[data-slideshow-target="slide"]').forEach((panel, index) => {
      panel.hidden = false;
      panel.dataset.buPrintStyle =
        index === 0 ? "Primary style" : index === 1 ? "Secondary style" : "Additional style";
    });
  });

  // Chrome and Safari normally derive the suggested PDF filename from the
  // document title. Include the run date and the detected Primary/Secondary
  // styles. Reloading naturally restores the site's original title.
  const date = new Date();
  const dateStamp = [
    date.getFullYear(),
    String(date.getMonth() + 1).padStart(2, "0"),
    String(date.getDate()).padStart(2, "0"),
  ].join("");
  const resultStyleNames = ["Primary style", "Secondary style"]
    .map((style) => {
      const panel = document.querySelector(
        `#section-1 [data-bu-print-style="${style}"]`,
      );
      return panel?.querySelector("h1, h2, h3, h4")?.textContent.trim();
    })
    .filter(Boolean);
  document.title = [
    dateStamp,
    "BlossomUp",
    reportTitle,
    resultStyleNames.join(", "),
  ]
    .filter(Boolean)
    .join(" - ");

  // Stack the two "met / not met" carousel groups before expanding their cards.
  const sectionFourSwipers = [...document.querySelectorAll("#section-4 .swiper")];
  const sectionFourGroups = commonAncestor(sectionFourSwipers);
  if (sectionFourGroups) sectionFourGroups.dataset.buPrintGroups = "";

  // Print widths with default margins can fall below the site's responsive
  // breakpoints. Mark the intended desktop structures so print CSS can retain
  // them independently of the browser's calculated page width.
  document.querySelectorAll("#section-1 .desktopSwiper").forEach((swiper) => {
    if (swiper.parentElement) swiper.parentElement.dataset.buPrintDesktopSwiper = "";
  });

  const barChart = document.querySelector('#section-3 [data-controller="bar-chart"]');
  const chartRow = barChart?.parentElement?.parentElement?.parentElement;
  if (chartRow) {
    chartRow.dataset.buPrintChartRow = "";
    chartRow.children[0]?.setAttribute("data-bu-print-chart-bar", "");
    chartRow.children[1]?.setAttribute("data-bu-print-chart-legend", "");
    chartRow.children[2]?.setAttribute("data-bu-print-chart-pie", "");
  }

  const famousImages = [...document.querySelectorAll("#section-10 img")];
  const famousGrid = commonAncestor(famousImages);
  if (famousGrid) {
    famousGrid.dataset.buPrintFamousGrid = "";
    [...famousGrid.children].forEach((card) => {
      card.dataset.buPrintFamousCard = "";
      card.querySelectorAll("img").forEach((image) => {
        image.dataset.buPrintFamousImage = "";
      });
    });
  }

  // Mark only the meaningful Complete Report boundaries: its three outer
  // chapters, the major articles within them, and each Love Style deep dive.
  // Internal headings such as Overview and The Breakdown remain continuous.
  const majorReportHeadings = [
    ...document.querySelectorAll("#report-content-sections > .relative > h1"),
    ...document.querySelectorAll(
      "#report-content-sections > .relative > .trix-content > h1",
    ),
    ...document.querySelectorAll(
      "#report-content-sections .subcontent > h1.subcontent",
    ),
  ];
  [...new Set(majorReportHeadings)].forEach((heading, index) => {
    heading.dataset.buPrintMajorHeading = "";
    if (!heading.id) heading.id = `bu-print-heading-${index + 1}`;
  });

  const reportChapterHeadings = [
    ...document.querySelectorAll("#report-content-sections > .relative > h1"),
  ];
  reportChapterHeadings[0]?.setAttribute("data-bu-print-no-break-before", "");
  reportChapterHeadings[2]?.nextElementSibling
    ?.querySelector(":scope > h1")
    ?.setAttribute("data-bu-print-no-break-before", "");

  // A print-only table of contents provides clickable internal links in PDF
  // exporters that preserve fragment links. Browser printing does not expose a
  // reliable way for page JavaScript to create a native PDF sidebar outline.
  const reportTocChildren = reportChapterHeadings.map((chapterHeading) => {
    const articleContainer = chapterHeading.nextElementSibling;
    const articleHeadings = articleContainer
      ? [
          ...articleContainer.querySelectorAll(
            ":scope > h1, :scope .subcontent > h1.subcontent",
          ),
        ]
      : [];
    return {
      id: chapterHeading.id,
      label: chapterHeading.textContent.trim(),
      children: articleHeadings.map((heading) => ({
        id: heading.id,
        label: heading.textContent.trim(),
      })),
    };
  });

  const personalisedTocSections = [
    { id: "section-0", label: "Your style for Expressing Love" },
    { id: "section-1", label: "About your Love Style" },
    { id: "section-2", label: "How you receive love" },
    { id: "section-3", label: "Results Summary" },
    { id: "section-4", label: "Adjectives" },
    { id: "section-6", label: "Why you crave it" },
    { id: "section-7", label: "Challenges" },
    { id: "section-8", label: "Strategies" },
    { id: "section-9", label: "Key Takeaways" },
    { id: "section-10", label: "Famous Examples" },
  ];
  const tocSections = [
    {
      label: "Personalised Results",
      children: personalisedTocSections,
    },
    {
      id: "section-11",
      label: "Reference Material: Complete Report",
      children: reportTocChildren,
    },
  ];
  const toc = document.createElement("nav");
  toc.id = "bu-print-toc";
  toc.setAttribute("aria-label", "Report contents");
  const tocHeading = document.createElement("h2");
  tocHeading.textContent = "Contents";
  const readingNote = document.createElement("aside");
  readingNote.id = "bu-print-reading-note";
  const readingNoteHeading = document.createElement("h3");
  readingNoteHeading.textContent = "How to read this report";
  const personalisedNote = document.createElement("p");
  personalisedNote.innerHTML =
    "<strong>Personalised results:</strong> this is the main custom part of your report. It uses your quiz results to present your scores and ranking, Primary and Secondary styles, charts, traits, challenges, strategies, takeaways, and related examples. Start here when you want the material most specific to you.";
  const referenceNote = document.createElement("p");
  referenceNote.innerHTML =
    "<strong>Reference material:</strong> Complete Report orders its eight Love Style deep dives to match the result ranking and labels the primary style, but its long descriptions and concluding articles are mostly reusable background guidance. This page contains no percentages or references to individual quiz answers inside Complete Report.";
  readingNote.append(readingNoteHeading, personalisedNote, referenceNote);

  const buildTocList = (entries) => {
    const list = document.createElement("ol");
    entries.forEach(({ id, label, children = [] }) => {
      if (id && !document.getElementById(id)) return;
      const item = document.createElement("li");
      if (children.length) item.classList.add("bu-toc-group");
      const labelElement = document.createElement(id ? "a" : "span");
      if (id) labelElement.href = `#${id}`;
      labelElement.textContent = label;
      item.appendChild(labelElement);
      if (children.length) item.appendChild(buildTocList(children));
      list.appendChild(item);
    });
    return list;
  };
  toc.append(tocHeading, readingNote, buildTocList(tocSections));
  hero?.after(toc);

  const css = `
    html, body {
      overflow: visible !important;
    }

    body {
      display: block !important;
      background: #fff !important;
    }

    body, body * {
      -webkit-print-color-adjust: exact !important;
      print-color-adjust: exact !important;
    }

    [data-bu-print-chrome],
    [data-bu-print-tabs] {
      display: none !important;
    }

    #bu-print-toc {
      display: none;
    }

    #section-11 [data-action*="toggle#showContent"] {
      cursor: default !important;
    }

    #section-11 [data-action*="toggle#showContent"] svg {
      display: none !important;
    }

    [data-bu-print-hero] {
      min-height: 0 !important;
      padding: 18px 24px !important;
    }

    [data-bu-print-hero] h3 {
      margin: 0 !important;
      padding: 0 !important;
      font-size: 32px !important;
      line-height: 1.15 !important;
    }

    #report,
    [data-controller~="slideshow"],
    [data-slideshow-target="slide"],
    .swiper,
    .swiper-wrapper {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    [data-slideshow-target="slide"] {
      display: block !important;
      visibility: visible !important;
      margin-top: 20px !important;
      padding-top: 14px !important;
      border-top: 2px solid #c9ddd5 !important;
    }

    [data-slideshow-target="slide"]::before {
      content: attr(data-bu-print-style);
      display: inline-block;
      margin: 0 0 12px !important;
      padding: 5px 10px;
      border-radius: 999px;
      background: #dfeee8;
      color: #1c3d4a;
      font-size: 14px;
      font-weight: 800;
      line-height: 1.2;
    }

    /* The About section contains desktop and mobile copies. Keep one copy. */
    #section-1 .reportSwiper {
      display: none !important;
    }

    [data-bu-print-groups] {
      display: grid !important;
      grid-template-columns: 1fr !important;
      gap: 18px !important;
    }

    .swiper-wrapper {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      gap: 14px !important;
      transform: none !important;
      transition: none !important;
    }

    .desktopSwiper .swiper-wrapper {
      grid-template-columns: 1fr !important;
    }

    .swiper-slide {
      box-sizing: border-box !important;
      display: block !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
      margin: 0 !important;
      opacity: 1 !important;
      visibility: visible !important;
      transform: none !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    .swiper-pagination,
    .pagination,
    .button-prev,
    .button-next,
    [class*="swiper-button"],
    [aria-label="Previous slide"],
    [aria-label="Next slide"],
    [aria-label^="Go to slide"],
    .swiper-notification {
      display: none !important;
    }

    img, svg {
      max-width: 100%;
    }

    @media (max-width: 760px) {
      .swiper-wrapper {
        grid-template-columns: 1fr !important;
      }
    }

    @media print {
      body {
        font-size: 10.5pt !important;
      }

      [data-bu-print-hero] h3 {
        font-size: 24pt !important;
      }

      a {
        color: inherit !important;
        text-decoration: none !important;
      }

      #bu-print-toc {
        display: block !important;
        max-width: 760px;
        margin: 10mm auto 0;
        padding: 8mm 10mm;
        border: 1px solid #c9ddd5;
        border-radius: 8px;
        background: #f6fbf9;
        break-after: page;
        page-break-after: always;
      }

      #bu-print-toc h2 {
        margin: 0 0 4mm;
        font-size: 22pt;
      }

      #bu-print-toc ol {
        columns: 1;
        margin: 0;
        padding-left: 6mm;
      }

      #bu-print-toc li {
        margin: 0 0 1.6mm;
        break-inside: avoid;
      }

      #bu-print-toc .bu-toc-group {
        break-inside: auto;
        page-break-inside: auto;
      }

      #bu-print-toc .bu-toc-group > a,
      #bu-print-toc .bu-toc-group > span {
        display: block;
        color: #1c3d4a !important;
        font-weight: 700;
        break-after: avoid;
        page-break-after: avoid;
      }

      #bu-print-toc li > ol {
        margin-top: 1.4mm;
        padding-left: 7mm;
        font-size: 9.5pt;
      }

      #bu-print-toc li > ol > li > ol {
        font-size: 9pt;
      }

      #bu-print-toc a {
        color: #1c3d4a !important;
        text-decoration: underline !important;
        text-underline-offset: 2px;
      }

      #bu-print-reading-note {
        margin: 0 0 5mm;
        padding: 4mm 5mm;
        border-left: 4px solid #67aa90;
        background: #fff;
        font-size: 9.5pt;
      }

      #bu-print-reading-note h3 {
        margin: 0 0 2mm;
        font-size: 12pt;
      }

      #bu-print-reading-note p {
        margin: 0 0 1.5mm;
      }

      #bu-print-reading-note p:last-child {
        margin-bottom: 0;
      }

      .swiper-wrapper {
        gap: 4mm !important;
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
        align-items: start !important;
      }

      .desktopSwiper .swiper-wrapper {
        grid-template-columns: 1fr !important;
      }

      [data-bu-print-desktop-swiper] {
        display: block !important;
      }

      [data-bu-print-chart-row] {
        display: grid !important;
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
        grid-template-areas:
          "bar pie"
          "legend legend" !important;
        justify-items: center !important;
        align-items: center !important;
        gap: 12px 20px !important;
      }

      [data-bu-print-chart-row] canvas {
        display: block !important;
        width: 100% !important;
        max-width: 100% !important;
        height: auto !important;
      }

      #section-3 [data-controller="bar-chart"] {
        max-width: 280px !important;
        aspect-ratio: 2 / 1 !important;
      }

      #section-3 [data-controller="pie-chart"] {
        width: 175px !important;
        max-width: 175px !important;
        height: 175px !important;
        max-height: 175px !important;
        aspect-ratio: 1 / 1 !important;
      }

      [data-bu-print-chart-bar] {
        grid-area: bar;
        width: 100% !important;
      }

      [data-bu-print-chart-legend] {
        grid-area: legend;
        width: 100% !important;
      }

      [data-bu-print-chart-legend] ul {
        display: grid !important;
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
        gap: 2px 18px !important;
      }

      [data-bu-print-chart-pie] {
        grid-area: pie;
      }

      [data-bu-print-famous-grid] {
        display: grid !important;
        grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
        justify-content: stretch !important;
        gap: 16px !important;
      }

      [data-bu-print-famous-card] {
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        width: auto !important;
        min-width: 0 !important;
        gap: 12px !important;
        break-inside: avoid;
        page-break-inside: avoid;
      }

      [data-bu-print-famous-image] {
        display: block !important;
        width: 100% !important;
        max-width: 280px !important;
        height: auto !important;
        aspect-ratio: 1 / 1;
        object-fit: cover !important;
      }

      .swiper-slide {
        align-self: start !important;
      }

      .swiper-slide > [class~="justify-between"] {
        flex: none !important;
        justify-content: flex-start !important;
      }

      [data-bu-print-style="Primary style"] {
        margin-top: 0 !important;
        padding-top: 6px !important;
      }

      [data-bu-print-style="Secondary style"] {
        break-before: page;
        page-break-before: always;
      }

      #section-5 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      #section-9 [data-controller~="slideshow"] {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr) 150px !important;
        align-items: end !important;
        gap: 16px !important;
      }

      #section-9 [data-bu-print-style="Secondary style"] {
        break-before: auto;
        page-break-before: auto;
      }

      #section-9 img {
        display: block !important;
        width: auto !important;
        max-width: 140px !important;
        height: auto !important;
        max-height: 180px !important;
        object-fit: contain !important;
      }

      [data-bu-print-major-heading] {
        break-before: page;
        page-break-before: always;
        break-after: avoid-page;
        page-break-after: avoid;
      }

      [data-bu-print-no-break-before] {
        break-before: auto !important;
        page-break-before: auto !important;
      }

      footer,
      [role="contentinfo"] {
        display: none !important;
      }

      [data-slideshow-target="slide"] {
        break-inside: auto;
        page-break-inside: auto;
      }

      /* Start each substantive report section on a clean page. Section 5 is a
         short decorative quote, so it stays attached to nearby content. */
      #section-1,
      #section-2,
      #section-3,
      #section-4,
      #section-6,
      #section-7,
      #section-8,
      #section-9,
      #section-10,
      #section-11 {
        break-before: page;
        page-break-before: always;
      }

      /* Ask the print engine to keep the shorter sections intact when they
         fit on one page. Taller sections still split naturally between cards. */
      #section-2,
      #section-3,
      #section-9,
      #section-10 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      p,
      li {
        orphans: 3;
        widows: 3;
      }

      #section-11 h1,
      #section-11 h2,
      #section-11 h3 {
        break-after: avoid;
        page-break-after: avoid;
      }
    }
  `;

  const style = document.createElement("style");
  style.id = STYLE_ID;
  style.textContent = css;
  document.head.appendChild(style);

  console.info(
    "BlossomUp print layout applied. Print with Cmd/Ctrl+P. Reload to restore the page.",
  );
})();

Sexual Values Report

Includes the follow-up fix that expands all three Potential Challenges cards instead of leaving only the first visible.

(() => {
  const STYLE_ID = "blossomup-sexual-values-print-layout";
  const EXPECTED_REPORT_TITLE = "Sexual Values Report";

  // This is a one-way, page-local transformation. Reload the page to reset it.
  if (document.getElementById(STYLE_ID)) {
    console.info("BlossomUp Sexual Values print layout is already applied. Reload to restore the page.");
    return;
  }

  // Derive the filename title from the visible report heading. The expected
  // title is only a safety check so this report-specific code fails closed.
  const reportHeading = [...document.querySelectorAll("#report h1, #report h2, #report h3")]
    .find((heading) => heading.textContent.trim());
  const reportTitle = reportHeading?.textContent.trim();
  if (reportTitle !== EXPECTED_REPORT_TITLE) {
    console.warn(
      `This snippet supports ${EXPECTED_REPORT_TITLE}, but detected ${
        reportTitle ? `“${reportTitle}”` : "no report title"
      }. Nothing was changed.`,
    );
    return;
  }

  const report = document.querySelector("#report");
  const hero = reportHeading.parentElement?.parentElement;
  if (hero) hero.dataset.buPrintHero = "";

  // Suppress app navigation, chat widgets, and other floating interface chrome.
  document.querySelectorAll("body *").forEach((element) => {
    if (["fixed", "sticky"].includes(getComputedStyle(element).position)) {
      element.dataset.buPrintChrome = "";
    }
  });

  // Each axis contains a mobile Swiper plus a complete three-card desktop row.
  // Keep the complete row and hide its duplicate carousel at every print width.
  document.querySelectorAll('#section-2 [data-controller~="slideshow"]').forEach((mobileAxis) => {
    mobileAxis.dataset.buPrintMobileAxis = "";
    const desktopAxis = mobileAxis.nextElementSibling;
    if (desktopAxis) desktopAxis.dataset.buPrintDesktopAxis = "";
    mobileAxis.parentElement?.setAttribute("data-bu-print-axis-group", "");
  });

  // Potential Challenges uses a hand-rolled horizontal scroller rather than
  // Swiper. At narrow print widths each card becomes full-width, leaving only
  // the first one visible. Convert all three challenge cards to a static grid.
  const challengesContainer = document.querySelector(
    "#section-4 .cards-container-custom-color",
  );
  const challengeCards = challengesContainer
    ? [...challengesContainer.querySelectorAll(".card-custom-color")]
    : [];
  const challengesGrid = challengeCards[0]?.parentElement;
  if (challengesContainer) challengesContainer.dataset.buPrintChallengesContainer = "";
  if (challengesGrid) challengesGrid.dataset.buPrintChallengesGrid = "";
  challengeCards.forEach((card) => {
    card.dataset.buPrintChallengeCard = "";
  });
  [...(challengesContainer?.children || [])]
    .filter((child) => challengesGrid && !child.contains(challengesGrid))
    .forEach((controls) => {
      controls.dataset.buPrintChallengesControls = "";
    });

  const sectionZeroLines = (document.querySelector("#section-0")?.innerText || "")
    .split("\n")
    .map((line) => line.trim())
    .filter(Boolean);
  const personaLabelIndex = sectionZeroLines.findIndex((line) =>
    /^Your Sexual Persona is:?$/i.test(line),
  );
  const persona = personaLabelIndex >= 0 ? sectionZeroLines[personaLabelIndex + 1] : "";

  const date = new Date();
  const dateStamp = [
    date.getFullYear(),
    String(date.getMonth() + 1).padStart(2, "0"),
    String(date.getDate()).padStart(2, "0"),
  ].join("");
  const safeTitlePart = (value) => value?.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim();
  document.title = [dateStamp, "BlossomUp", reportTitle, persona]
    .map(safeTitlePart)
    .filter(Boolean)
    .join(" - ");

  const tocSections = [
    { id: "section-0", label: persona ? `Your Sexual Persona: ${persona}` : "Your Sexual Persona" },
    { id: "section-1", label: "Sexual Values" },
    { id: "section-2", label: "Deep Dive into the Axes" },
    { id: "section-3", label: "Sexual Priorities" },
    { id: "section-4", label: "Potential Challenges" },
  ];
  const toc = document.createElement("nav");
  toc.id = "bu-print-toc";
  toc.setAttribute("aria-label", "Report contents");
  const tocHeading = document.createElement("h2");
  tocHeading.textContent = "Contents";
  const tocList = document.createElement("ol");
  tocSections.forEach(({ id, label }) => {
    const section = document.getElementById(id);
    if (!section) return;
    const item = document.createElement("li");
    const link = document.createElement("a");
    link.href = `#${id}`;
    link.textContent = label;
    item.appendChild(link);
    tocList.appendChild(item);
  });
  toc.append(tocHeading, tocList);
  hero?.after(toc);

  const css = `
    html, body,
    #report {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    body {
      display: block !important;
      background: #fff !important;
    }

    body, body * {
      -webkit-print-color-adjust: exact !important;
      print-color-adjust: exact !important;
    }

    [data-bu-print-chrome],
    [data-bu-print-mobile-axis],
    [data-bu-print-challenges-controls],
    .swiper-pagination,
    .pagination,
    .button-prev,
    .button-next,
    [class*="swiper-button"],
    [aria-label="Previous slide"],
    [aria-label="Next slide"],
    [aria-label^="Go to slide"],
    .swiper-notification {
      display: none !important;
    }

    [data-bu-print-desktop-axis] {
      display: grid !important;
      grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
      gap: 16px !important;
      width: 100% !important;
      max-width: none !important;
    }

    [data-bu-print-desktop-axis] > * {
      box-sizing: border-box !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    [data-bu-print-challenges-container],
    [data-bu-print-challenges-grid] {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    [data-bu-print-challenges-grid] {
      display: grid !important;
      grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
      align-items: stretch !important;
      gap: 16px !important;
      scroll-snap-type: none !important;
    }

    [data-bu-print-challenge-card] {
      box-sizing: border-box !important;
      display: flex !important;
      width: auto !important;
      min-width: 0 !important;
      max-width: none !important;
      height: auto !important;
      margin: 0 !important;
      flex: none !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    [data-bu-print-hero] {
      min-height: 0 !important;
      padding: 18px 24px !important;
    }

    [data-bu-print-hero] h3 {
      margin: 0 !important;
      padding: 0 !important;
      font-size: 32px !important;
      line-height: 1.15 !important;
    }

    #bu-print-toc {
      display: none;
    }

    img, svg, canvas {
      max-width: 100% !important;
      height: auto;
    }

    @media (max-width: 700px) {
      [data-bu-print-desktop-axis],
      [data-bu-print-challenges-grid] {
        grid-template-columns: 1fr !important;
      }
    }

    @media print {
      body {
        font-size: 10.5pt !important;
      }

      [data-bu-print-hero] h3 {
        font-size: 24pt !important;
      }

      a {
        color: inherit !important;
        text-decoration: none !important;
      }

      #bu-print-toc {
        display: block !important;
        max-width: 170mm;
        margin: 8mm auto 0;
        padding: 8mm 10mm;
        border: 1px solid #c9ddd5;
        border-radius: 8px;
        background: #f6fbf9;
        break-after: page;
        page-break-after: always;
      }

      #bu-print-toc h2 {
        margin: 0 0 4mm;
        font-size: 22pt;
      }

      #bu-print-toc ol {
        margin: 0;
        padding-left: 6mm;
      }

      #bu-print-toc li {
        margin: 0 0 2mm;
      }

      #bu-print-toc a {
        color: #1c3d4a !important;
        text-decoration: underline !important;
        text-underline-offset: 2px;
      }

      [data-bu-print-desktop-axis] {
        gap: 4mm !important;
      }

      [data-bu-print-challenges-container] {
        padding: 0 !important;
      }

      [data-bu-print-challenges-grid] {
        grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
        gap: 4mm !important;
      }

      [data-bu-print-axis-group] {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      #section-1,
      #section-2,
      #section-3,
      #section-4 {
        break-before: page;
        page-break-before: always;
      }

      #section-0,
      #section-1,
      #section-3,
      #section-4 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      h1, h2, h3, h4 {
        break-after: avoid;
        page-break-after: avoid;
      }

      p, li {
        orphans: 3;
        widows: 3;
      }

      footer,
      [role="contentinfo"] {
        display: none !important;
      }
    }
  `;

  const style = document.createElement("style");
  style.id = STYLE_ID;
  style.textContent = css;
  document.head.appendChild(style);

  console.info(
    `BlossomUp ${reportTitle} print layout applied. Print with Cmd/Ctrl+P. Reload to restore the page.`,
  );
})();

Conflict Types Report

Expands the reaction carousel and keeps the complete desktop advice layout.

(() => {
  const STYLE_ID = "blossomup-conflict-types-print-layout";
  const EXPECTED_REPORT_TITLE = "Conflict Types Report";

  // This is a one-way, page-local transformation. Reload the page to reset it.
  if (document.getElementById(STYLE_ID)) {
    console.info("BlossomUp Conflict Types print layout is already applied. Reload to restore the page.");
    return;
  }

  // Derive the filename title from the visible report heading. The expected
  // title is only a safety check so this report-specific code fails closed.
  const reportHeading = [...document.querySelectorAll("#report h1, #report h2, #report h3")]
    .find((heading) => heading.textContent.trim());
  const reportTitle = reportHeading?.textContent.trim();
  if (reportTitle !== EXPECTED_REPORT_TITLE) {
    console.warn(
      `This snippet supports ${EXPECTED_REPORT_TITLE}, but detected ${
        reportTitle ? `“${reportTitle}”` : "no report title"
      }. Nothing was changed.`,
    );
    return;
  }

  const report = document.querySelector("#report");
  const hero = reportHeading.parentElement?.parentElement;
  if (hero) hero.dataset.buPrintHero = "";

  // Suppress app navigation, chat widgets, and other floating interface chrome.
  document.querySelectorAll("body *").forEach((element) => {
    if (["fixed", "sticky"].includes(getComputedStyle(element).position)) {
      element.dataset.buPrintChrome = "";
    }
  });

  // The reaction section is a genuine five-card carousel with no desktop copy.
  // Convert its wrapper into a static grid and hide all carousel controls.
  const reactionSwiper = document.querySelector("#section-2 .cardsSwiper");
  if (reactionSwiper) reactionSwiper.dataset.buPrintStaticCards = "";

  // The final advice section contains both a full desktop grid and a duplicate
  // mobile Swiper. Keep the complete desktop grid at every print width.
  const adviceSlide = document.querySelector('#section-5 [data-slideshow-target~="slide"]');
  const mobileAdvice = document.querySelector("#section-5 .reportSwiper")?.parentElement;
  const desktopAdvice = adviceSlide
    ? [...adviceSlide.children].find((child) => child !== mobileAdvice)
    : null;
  if (mobileAdvice) mobileAdvice.dataset.buPrintMobileAdvice = "";
  if (desktopAdvice) desktopAdvice.dataset.buPrintDesktopAdvice = "";

  const sectionOneLayout = document.querySelector("#section-1 > :last-child");
  const sectionThreeLayout = document.querySelector("#section-3 > :last-child");
  const sectionFourLayout = document.querySelector("#section-4 > :last-child");
  if (sectionOneLayout) sectionOneLayout.dataset.buPrintTwoColumn = "model";
  if (sectionThreeLayout) sectionThreeLayout.dataset.buPrintTwoColumn = "strengths";
  if (sectionFourLayout) sectionFourLayout.dataset.buPrintTwoColumn = "experience";

  const dominantHeading = [...document.querySelectorAll("#section-0 h1, #section-0 h2, #section-0 h3")]
    .find((heading) => /^Your dominant conflict style is:/i.test(heading.textContent.trim()));
  const dominantText = dominantHeading?.textContent.replace(/\s+/g, " ").trim();
  const conflictType = dominantText?.split(":").slice(1).join(":").trim();

  const date = new Date();
  const dateStamp = [
    date.getFullYear(),
    String(date.getMonth() + 1).padStart(2, "0"),
    String(date.getDate()).padStart(2, "0"),
  ].join("");
  const safeTitlePart = (value) => value?.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim();
  document.title = [dateStamp, "BlossomUp", reportTitle, conflictType]
    .map(safeTitlePart)
    .filter(Boolean)
    .join(" - ");

  // The site's own navigation omits section 3, so include it explicitly here.
  const tocSections = [
    {
      id: "section-0",
      label: conflictType
        ? `Your Dominant Conflict Style: ${conflictType}`
        : "Your Dominant Conflict Style",
    },
    { id: "section-1", label: "What Triggers This Style" },
    { id: "section-2", label: "How You React When Tension Appears" },
    { id: "section-3", label: "Your Strengths and Blind Spots in Conflict" },
    { id: "section-4", label: "How Others Experience You" },
    { id: "section-5", label: "How to Improve Conflict Outcomes" },
  ];
  const toc = document.createElement("nav");
  toc.id = "bu-print-toc";
  toc.setAttribute("aria-label", "Report contents");
  const tocHeading = document.createElement("h2");
  tocHeading.textContent = "Contents";
  const tocList = document.createElement("ol");
  tocSections.forEach(({ id, label }) => {
    const section = document.getElementById(id);
    if (!section) return;
    const item = document.createElement("li");
    const link = document.createElement("a");
    link.href = `#${id}`;
    link.textContent = label;
    item.appendChild(link);
    tocList.appendChild(item);
  });
  toc.append(tocHeading, tocList);
  hero?.after(toc);

  const css = `
    html, body,
    #report {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    body {
      display: block !important;
      background: #fff !important;
    }

    body, body * {
      -webkit-print-color-adjust: exact !important;
      print-color-adjust: exact !important;
    }

    [data-bu-print-chrome],
    [data-bu-print-mobile-advice],
    .swiper-pagination,
    .pagination,
    .button-prev,
    .button-next,
    [class*="swiper-button"],
    [aria-label="Previous slide"],
    [aria-label="Next slide"],
    [aria-label^="Go to slide"],
    .swiper-notification {
      display: none !important;
    }

    [data-bu-print-static-cards],
    [data-bu-print-static-cards] .swiper-wrapper {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
      transform: none !important;
    }

    [data-bu-print-static-cards] .swiper-wrapper {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: stretch !important;
      gap: 16px !important;
    }

    [data-bu-print-static-cards] .swiper-slide {
      box-sizing: border-box !important;
      display: block !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
      margin: 0 !important;
      opacity: 1 !important;
      visibility: visible !important;
      transform: none !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    [data-bu-print-desktop-advice] {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      gap: 16px !important;
      width: 100% !important;
      max-width: none !important;
    }

    [data-bu-print-desktop-advice] > * {
      box-sizing: border-box !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    [data-bu-print-two-column] {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: start !important;
      gap: 24px !important;
    }

    [data-bu-print-two-column="model"] {
      grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr) !important;
    }

    [data-bu-print-two-column] > * {
      box-sizing: border-box !important;
      min-width: 0 !important;
      width: auto !important;
    }

    [data-bu-print-hero] {
      min-height: 0 !important;
      padding: 18px 24px !important;
    }

    [data-bu-print-hero] h3 {
      margin: 0 !important;
      padding: 0 !important;
      font-size: 32px !important;
      line-height: 1.15 !important;
    }

    #bu-print-toc {
      display: none;
    }

    img, svg, canvas {
      max-width: 100% !important;
      height: auto;
    }

    @media (max-width: 700px) {
      [data-bu-print-static-cards] .swiper-wrapper,
      [data-bu-print-desktop-advice],
      [data-bu-print-two-column] {
        grid-template-columns: 1fr !important;
      }
    }

    @media print {
      body {
        font-size: 10.5pt !important;
      }

      [data-bu-print-hero] h3 {
        font-size: 24pt !important;
      }

      a {
        color: inherit !important;
        text-decoration: none !important;
      }

      #bu-print-toc {
        display: block !important;
        max-width: 170mm;
        margin: 8mm auto 0;
        padding: 8mm 10mm;
        border: 1px solid #c9ddd5;
        border-radius: 8px;
        background: #f6fbf9;
        break-after: page;
        page-break-after: always;
      }

      #bu-print-toc h2 {
        margin: 0 0 4mm;
        font-size: 22pt;
      }

      #bu-print-toc ol {
        margin: 0;
        padding-left: 6mm;
      }

      #bu-print-toc li {
        margin: 0 0 2mm;
      }

      #bu-print-toc a {
        color: #1c3d4a !important;
        text-decoration: underline !important;
        text-underline-offset: 2px;
      }

      [data-bu-print-static-cards] .swiper-wrapper,
      [data-bu-print-desktop-advice] {
        gap: 4mm !important;
      }

      [data-bu-print-two-column] {
        gap: 5mm !important;
      }

      #section-1,
      #section-2,
      #section-3,
      #section-4,
      #section-5 {
        break-before: page;
        page-break-before: always;
      }

      #section-0,
      #section-1,
      #section-3,
      #section-4 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      h1, h2, h3, h4 {
        break-after: avoid;
        page-break-after: avoid;
      }

      p, li {
        orphans: 3;
        widows: 3;
      }

      footer,
      [role="contentinfo"] {
        display: none !important;
      }
    }
  `;

  const style = document.createElement("style");
  style.id = STYLE_ID;
  style.textContent = css;
  document.head.appendChild(style);

  console.info(
    `BlossomUp ${reportTitle} print layout applied. Print with Cmd/Ctrl+P. Reload to restore the page.`,
  );
})();

BPD Report

Expands the short profile and long Complete Report into a navigable print layout.

(() => {
  const STYLE_ID = "blossomup-bpd-print-layout";
  const EXPECTED_REPORT_TITLE = "BPD Report";

  // This is a one-way, page-local transformation. Reload the page to reset it.
  if (document.getElementById(STYLE_ID)) {
    console.info("BlossomUp BPD print layout is already applied. Reload to restore the page.");
    return;
  }

  // Derive the filename title from the visible report heading. The expected
  // title is only a safety check so this report-specific code fails closed.
  const reportHeading = [...document.querySelectorAll("#report h1, #report h2, #report h3")]
    .find((heading) => heading.textContent.trim());
  const reportTitle = reportHeading?.textContent.trim();
  if (reportTitle !== EXPECTED_REPORT_TITLE) {
    console.warn(
      `This snippet supports ${EXPECTED_REPORT_TITLE}, but detected ${
        reportTitle ? `“${reportTitle}”` : "no report title"
      }. Nothing was changed.`,
    );
    return;
  }

  const hero = reportHeading.parentElement?.parentElement;
  if (hero) hero.dataset.buPrintHero = "";

  // Suppress app navigation, chat widgets, and other floating interface chrome.
  document.querySelectorAll("body *").forEach((element) => {
    if (["fixed", "sticky"].includes(getComputedStyle(element).position)) {
      element.dataset.buPrintChrome = "";
    }
  });

  const normalizedText = (element) => element?.textContent.replace(/\s+/g, " ").trim();
  const primaryHeading = [...document.querySelectorAll("#section-0 h1, #section-0 h2, #section-0 h3")]
    .find((heading) => /^Your BPD is:/i.test(normalizedText(heading)));
  const primaryText = normalizedText(primaryHeading);
  const primaryType = primaryText?.split(":").slice(1).join(":").trim();
  const secondaryElement = [...document.querySelectorAll("#section-0 p")]
    .find((element) => /^Secondary:/i.test(normalizedText(element)));
  const secondaryText = normalizedText(secondaryElement);
  const secondaryType = secondaryText?.split(":").slice(1).join(":").trim();

  // Preserve the intended two-column profile-card layout at narrow print
  // widths, where the site's responsive CSS would otherwise stack the cards.
  ["#section-1 > :last-child", "#section-2 > :last-child"].forEach((selector) => {
    document.querySelector(selector)?.setAttribute("data-bu-print-profile-grid", "");
  });

  // Always expose the long report and remove the interactive collapse affordance.
  const completeReport = document.querySelector("#report-content-sections");
  if (completeReport) completeReport.dataset.buPrintCompleteReport = "";
  document.querySelector("#report-content")?.setAttribute("data-bu-print-complete-heading", "");

  // Add stable anchors for both the short profile cards and the long report.
  const profileHeadings = [...document.querySelectorAll(
    "#section-1 h1, #section-1 h2, #section-1 h3, #section-1 h4, " +
      "#section-2 h1, #section-2 h2, #section-2 h3, #section-2 h4",
  )].filter((heading) => {
    const text = normalizedText(heading);
    return text && text !== "Profile Breakdown";
  });
  profileHeadings.forEach((heading, index) => {
    if (!heading.id) heading.id = `bu-bpd-profile-${index + 1}`;
  });

  const completeHeadings = completeReport
    ? [...completeReport.querySelectorAll("h1.subcontent")].filter((heading) => normalizedText(heading))
    : [];
  completeHeadings.forEach((heading, index) => {
    if (!heading.id) heading.id = `bu-bpd-complete-${index + 1}`;
    heading.dataset.buPrintMajorHeading = "";
    if (index === 0) heading.dataset.buPrintNoBreakBefore = "";
  });

  // Chrome and Safari normally derive the suggested PDF filename from the
  // document title. Use the page's report title and detected result types.
  const date = new Date();
  const dateStamp = [
    date.getFullYear(),
    String(date.getMonth() + 1).padStart(2, "0"),
    String(date.getDate()).padStart(2, "0"),
  ].join("");
  const safeTitlePart = (value) => value?.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim();
  const resultTypes = [primaryType, secondaryType].filter(Boolean).join(", ");
  document.title = [dateStamp, "BlossomUp", reportTitle, resultTypes]
    .map(safeTitlePart)
    .filter(Boolean)
    .join(" - ");

  const tocSections = [
    {
      label: "Personalised Results",
      children: [
        {
          id: "section-0",
          label: primaryType
            ? `Your BPD Profile: ${primaryType}${secondaryType ? ` (Secondary: ${secondaryType})` : ""}`
            : "Your BPD Profile",
        },
        {
          id: "section-1",
          label: "Profile Breakdown",
          children: profileHeadings.map((heading) => ({
            id: heading.id,
            label: normalizedText(heading),
          })),
        },
      ],
    },
    {
      id: "section-3",
      label: "Reference Material: Complete Report",
      children: completeHeadings.map((heading) => ({
        id: heading.id,
        label: normalizedText(heading),
      })),
    },
  ];

  const toc = document.createElement("nav");
  toc.id = "bu-print-toc";
  toc.setAttribute("aria-label", "Report contents");
  const tocHeading = document.createElement("h2");
  tocHeading.textContent = "Contents";
  const readingNote = document.createElement("aside");
  readingNote.id = "bu-print-reading-note";
  const readingNoteHeading = document.createElement("h3");
  readingNoteHeading.textContent = "How to read this report";
  const personalisedNote = document.createElement("p");
  personalisedNote.innerHTML =
    "<strong>Personalised results:</strong> the main result specific to this quiz is the Primary and Secondary type selection at the start. The Profile Breakdown then gives short summaries for the Primary type across relationships, family, friendships, and communication.";
  const referenceNote = document.createElement("p");
  referenceNote.innerHTML =
    "<strong>Reference material:</strong> Complete Report expands the Primary type with longer general descriptions. It repeats the opening profile material before adding further background sections such as Personal Growth and Recap; it does not identify individual quiz answers.";
  readingNote.append(readingNoteHeading, personalisedNote, referenceNote);

  const buildTocList = (entries) => {
    const list = document.createElement("ol");
    entries.forEach(({ id, label, children = [] }) => {
      if (id && !document.getElementById(id)) return;
      const item = document.createElement("li");
      if (children.length) item.classList.add("bu-toc-group");
      const labelElement = document.createElement(id ? "a" : "span");
      if (id) labelElement.href = `#${id}`;
      labelElement.textContent = label;
      item.appendChild(labelElement);
      if (children.length) item.appendChild(buildTocList(children));
      list.appendChild(item);
    });
    return list;
  };
  toc.append(tocHeading, readingNote, buildTocList(tocSections));
  hero?.after(toc);

  const css = `
    html, body,
    #report,
    [data-bu-print-complete-report] {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    body {
      display: block !important;
      background: #fff !important;
    }

    body, body * {
      -webkit-print-color-adjust: exact !important;
      print-color-adjust: exact !important;
    }

    [data-bu-print-chrome] {
      display: none !important;
    }

    [data-bu-print-complete-report] {
      display: block !important;
      visibility: visible !important;
      margin-top: 16px !important;
    }

    [data-bu-print-complete-heading] {
      cursor: default !important;
    }

    [data-bu-print-complete-heading] svg {
      display: none !important;
    }

    [data-bu-print-profile-grid] {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: stretch !important;
      gap: 16px !important;
    }

    [data-bu-print-profile-grid] > * {
      box-sizing: border-box !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
    }

    [data-bu-print-hero] {
      min-height: 0 !important;
      padding: 18px 24px !important;
    }

    [data-bu-print-hero] h3 {
      margin: 0 !important;
      padding: 0 !important;
      font-size: 32px !important;
      line-height: 1.15 !important;
    }

    #bu-print-toc {
      display: none;
    }

    img, svg, canvas {
      max-width: 100% !important;
      height: auto;
    }

    @media (max-width: 700px) {
      [data-bu-print-profile-grid] {
        grid-template-columns: 1fr !important;
      }
    }

    @media print {
      body {
        font-size: 10.5pt !important;
      }

      [data-bu-print-hero] h3 {
        font-size: 24pt !important;
      }

      a {
        color: inherit !important;
        text-decoration: none !important;
      }

      #bu-print-toc {
        display: block !important;
        max-width: 170mm;
        margin: 8mm auto 0;
        padding: 8mm 10mm;
        border: 1px solid #c9ddd5;
        border-radius: 8px;
        background: #f6fbf9;
        break-after: page;
        page-break-after: always;
      }

      #bu-print-toc h2 {
        margin: 0 0 4mm;
        font-size: 22pt;
      }

      #bu-print-toc ol {
        margin: 0;
        padding-left: 6mm;
      }

      #bu-print-toc li {
        margin: 0 0 1.8mm;
        break-inside: avoid;
      }

      #bu-print-toc .bu-toc-group {
        break-inside: auto;
        page-break-inside: auto;
      }

      #bu-print-toc .bu-toc-group > a,
      #bu-print-toc .bu-toc-group > span {
        display: block;
        color: #1c3d4a !important;
        font-weight: 700;
        break-after: avoid;
        page-break-after: avoid;
      }

      #bu-print-toc li > ol {
        margin-top: 1.2mm;
        padding-left: 7mm;
        font-size: 9.5pt;
      }

      #bu-print-toc a {
        color: #1c3d4a !important;
        text-decoration: underline !important;
        text-underline-offset: 2px;
      }

      #bu-print-reading-note {
        margin: 0 0 5mm;
        padding: 4mm 5mm;
        border-left: 4px solid #67aa90;
        background: #fff;
        font-size: 9.5pt;
      }

      #bu-print-reading-note h3 {
        margin: 0 0 2mm;
        font-size: 12pt;
      }

      #bu-print-reading-note p {
        margin: 0 0 1.5mm;
      }

      #bu-print-reading-note p:last-child {
        margin-bottom: 0;
      }

      [data-bu-print-profile-grid] {
        gap: 4mm !important;
      }

      [data-bu-print-profile-grid] > * {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      #section-1,
      #section-3 {
        break-before: page;
        page-break-before: always;
      }

      #section-0,
      #section-2 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      [data-bu-print-complete-heading] {
        break-after: avoid-page;
        page-break-after: avoid;
      }

      [data-bu-print-complete-report] {
        margin-top: 4mm !important;
      }

      [data-bu-print-major-heading] {
        break-before: page;
        page-break-before: always;
        break-after: avoid;
        page-break-after: avoid;
      }

      [data-bu-print-no-break-before] {
        break-before: auto !important;
        page-break-before: auto !important;
      }

      h1, h2, h3, h4 {
        break-after: avoid;
        page-break-after: avoid;
      }

      p, li {
        orphans: 3;
        widows: 3;
      }

      footer,
      [role="contentinfo"] {
        display: none !important;
      }
    }
  `;

  const style = document.createElement("style");
  style.id = STYLE_ID;
  style.textContent = css;
  document.head.appendChild(style);

  console.info(
    `BlossomUp ${reportTitle} print layout applied. Print with Cmd/Ctrl+P. Reload to restore the page.`,
  );
})();

Narcissist Report

Expands style tabs, trait and pattern carousels, and the long Complete Report.

(() => {
  const STYLE_ID = "blossomup-narcissist-print-layout";
  const EXPECTED_REPORT_TITLE = "Narcissist Report";

  // This is a one-way, page-local transformation. Reload the page to reset it.
  if (document.getElementById(STYLE_ID)) {
    console.info("BlossomUp Narcissist print layout is already applied. Reload to restore the page.");
    return;
  }

  // Derive the filename title from the visible report heading. The expected
  // title is only a safety check so this report-specific code fails closed.
  const reportHeading = [...document.querySelectorAll("#report h1, #report h2, #report h3")]
    .find((heading) => heading.textContent.trim());
  const reportTitle = reportHeading?.textContent.trim();
  if (reportTitle !== EXPECTED_REPORT_TITLE) {
    console.warn(
      `This snippet supports ${EXPECTED_REPORT_TITLE}, but detected ${
        reportTitle ? `“${reportTitle}”` : "no report title"
      }. Nothing was changed.`,
    );
    return;
  }

  const normalizedText = (element) => element?.textContent.replace(/\s+/g, " ").trim();
  const commonAncestor = (elements) => {
    if (!elements.length) return null;
    for (let candidate = elements[0]; candidate; candidate = candidate.parentElement) {
      if (elements.every((element) => candidate.contains(element))) return candidate;
    }
    return null;
  };

  const hero = reportHeading.parentElement?.parentElement;
  if (hero) hero.dataset.buPrintHero = "";

  // Suppress app navigation, chat widgets, and other floating interface chrome.
  document.querySelectorAll("body *").forEach((element) => {
    if (["fixed", "sticky"].includes(getComputedStyle(element).position)) {
      element.dataset.buPrintChrome = "";
    }
  });

  // Reveal both Primary and Secondary panels, replace the interactive tabs
  // with static labels, and turn all trait/pattern carousels into card grids.
  const panelEntries = [];
  document.querySelectorAll('#report [data-controller~="slideshow"]').forEach((controller) => {
    const tabs = [...controller.querySelectorAll('[data-slideshow-target~="style"]')];
    const tabRow = commonAncestor(tabs);
    if (tabRow && tabRow !== controller) tabRow.dataset.buPrintTabs = "";

    const panels = [...controller.querySelectorAll('[data-slideshow-target~="slide"]')];
    panels.forEach((panel, index) => {
      panel.hidden = false;
      panel.dataset.buPrintStyle = index === 0 ? "Primary style" : "Secondary style";
      if (!panel.id) panel.id = `bu-narcissist-panel-${panelEntries.length + 1}`;
      panel.querySelectorAll(".swiper").forEach((swiper) => {
        swiper.dataset.buPrintCarousel = "";
      });
      panelEntries.push({
        id: panel.id,
        label: `${panel.dataset.buPrintStyle}: ${normalizedText(panel.querySelector("h1, h2, h3, h4"))}`,
        sectionId: panel.closest('[id^="section-"]')?.id,
      });
    });
  });

  const copingController = document.querySelector('#section-4 [data-controller~="slideshow"]');
  const copingPanels = copingController
    ? [...copingController.querySelectorAll('[data-slideshow-target~="slide"]')]
    : [];
  const copingPanelHost = commonAncestor(copingPanels);
  if (copingController) copingController.dataset.buPrintCopingController = "";
  if (copingPanelHost && copingPanelHost !== copingController) {
    copingPanelHost.dataset.buPrintCopingPanels = "";
  }
  copingController?.querySelectorAll("img").forEach((image) => {
    image.dataset.buPrintCopingImage = "";
  });

  const insightGrid = document.querySelector("#section-3 > :last-child");
  if (insightGrid) insightGrid.dataset.buPrintInsightGrid = "";

  // Always expose the long report and remove the interactive collapse affordance.
  const completeReport = document.querySelector("#report-content-sections");
  if (completeReport) completeReport.dataset.buPrintCompleteReport = "";
  document.querySelector("#report-content")?.setAttribute("data-bu-print-complete-heading", "");

  const completeHeadings = completeReport
    ? [...completeReport.querySelectorAll("h1.subcontent")].filter((heading) => normalizedText(heading))
    : [];
  completeHeadings.forEach((heading, index) => {
    if (!heading.id) heading.id = `bu-narcissist-complete-${index + 1}`;
    heading.dataset.buPrintMajorHeading = "";
    if (index === 0) heading.dataset.buPrintNoBreakBefore = "";
  });

  const sectionOnePanels = [...document.querySelectorAll(
    '#section-1 [data-slideshow-target~="slide"]',
  )];
  const resultStyleNames = sectionOnePanels
    .map((panel) => normalizedText(panel.querySelector("h1, h2, h3, h4")))
    .filter(Boolean);
  const primaryStyle = resultStyleNames[0] || "";
  const secondaryStyle = resultStyleNames[1] || "";

  // Chrome and Safari normally derive the suggested PDF filename from the
  // document title. Use the page's report title and detected result styles.
  const date = new Date();
  const dateStamp = [
    date.getFullYear(),
    String(date.getMonth() + 1).padStart(2, "0"),
    String(date.getDate()).padStart(2, "0"),
  ].join("");
  const safeTitlePart = (value) => value?.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim();
  document.title = [dateStamp, "BlossomUp", reportTitle, resultStyleNames.join(", ")]
    .map(safeTitlePart)
    .filter(Boolean)
    .join(" - ");

  const panelsForSection = (sectionId) => panelEntries
    .filter((entry) => entry.sectionId === sectionId)
    .map(({ id, label }) => ({ id, label }));
  const tocSections = [
    {
      label: "Personalised Results",
      children: [
        {
          id: "section-0",
          label: primaryStyle
            ? `Your Narcissist Profile: ${primaryStyle}${secondaryStyle ? ` (Secondary: ${secondaryStyle})` : ""}`
            : "Your Narcissist Profile",
        },
        { id: "section-1", label: "Key Traits", children: panelsForSection("section-1") },
        {
          id: "section-2",
          label: "Behavioral Patterns",
          children: panelsForSection("section-2"),
        },
        { id: "section-3", label: "Psychological Insights" },
        {
          id: "section-4",
          label: "Coping Strategies",
          children: panelsForSection("section-4"),
        },
      ],
    },
    {
      id: "section-5",
      label: "Reference Material: Complete Report",
      children: completeHeadings.map((heading) => ({
        id: heading.id,
        label: normalizedText(heading),
      })),
    },
  ];

  const toc = document.createElement("nav");
  toc.id = "bu-print-toc";
  toc.setAttribute("aria-label", "Report contents");
  const tocHeading = document.createElement("h2");
  tocHeading.textContent = "Contents";
  const readingNote = document.createElement("aside");
  readingNote.id = "bu-print-reading-note";
  const readingNoteHeading = document.createElement("h3");
  readingNoteHeading.textContent = "How to read this report";
  const cautionNote = document.createElement("p");
  cautionNote.innerHTML =
    "<strong>Quiz context:</strong> this is a quiz-generated personality profile, not a clinical diagnosis.";
  const personalisedNote = document.createElement("p");
  personalisedNote.innerHTML =
    "<strong>Personalised results:</strong> the main result specific to this quiz is the Primary and Secondary style selection. The short report then compares those styles across traits, behavioural patterns, psychological insights, and coping strategies.";
  const referenceNote = document.createElement("p");
  referenceNote.innerHTML =
    "<strong>Reference material:</strong> Complete Report expands the Primary style with longer general descriptions covering its traits, communication, effects, and coping guidance; it does not identify individual quiz answers.";
  readingNote.append(readingNoteHeading, cautionNote, personalisedNote, referenceNote);

  const buildTocList = (entries) => {
    const list = document.createElement("ol");
    entries.forEach(({ id, label, children = [] }) => {
      if (id && !document.getElementById(id)) return;
      const item = document.createElement("li");
      if (children.length) item.classList.add("bu-toc-group");
      const labelElement = document.createElement(id ? "a" : "span");
      if (id) labelElement.href = `#${id}`;
      labelElement.textContent = label;
      item.appendChild(labelElement);
      if (children.length) item.appendChild(buildTocList(children));
      list.appendChild(item);
    });
    return list;
  };
  toc.append(tocHeading, readingNote, buildTocList(tocSections));
  hero?.after(toc);

  const css = `
    html, body,
    #report,
    [data-controller~="slideshow"],
    [data-slideshow-target~="slide"],
    [data-bu-print-carousel],
    [data-bu-print-carousel] .swiper-wrapper,
    [data-bu-print-complete-report] {
      width: 100% !important;
      max-width: none !important;
      height: auto !important;
      overflow: visible !important;
    }

    body {
      display: block !important;
      background: #fff !important;
    }

    body, body * {
      -webkit-print-color-adjust: exact !important;
      print-color-adjust: exact !important;
    }

    [data-bu-print-chrome],
    [data-bu-print-tabs],
    .swiper-pagination,
    .pagination,
    .button-prev,
    .button-next,
    [class*="swiper-button"],
    [aria-label="Previous slide"],
    [aria-label="Next slide"],
    [aria-label^="Go to slide"],
    .swiper-notification {
      display: none !important;
    }

    [data-slideshow-target~="slide"] {
      display: block !important;
      visibility: visible !important;
      margin-top: 20px !important;
      padding-top: 14px !important;
      border-top: 2px solid #c9ddd5 !important;
    }

    [data-slideshow-target~="slide"]::before {
      content: attr(data-bu-print-style);
      display: inline-block;
      margin: 0 0 12px !important;
      padding: 5px 10px;
      border-radius: 999px;
      background: #dfeee8;
      color: #1c3d4a;
      font-size: 14px;
      font-weight: 800;
      line-height: 1.2;
    }

    [data-bu-print-carousel] .swiper-wrapper {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: stretch !important;
      gap: 16px !important;
      transform: none !important;
      transition: none !important;
    }

    [data-bu-print-carousel] .swiper-slide {
      box-sizing: border-box !important;
      display: block !important;
      width: auto !important;
      min-width: 0 !important;
      height: auto !important;
      margin: 0 !important;
      opacity: 1 !important;
      visibility: visible !important;
      transform: none !important;
      break-inside: avoid;
      page-break-inside: avoid;
    }

    [data-bu-print-insight-grid] {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: stretch !important;
      gap: 16px !important;
    }

    [data-bu-print-coping-controller] {
      display: grid !important;
      grid-template-columns: minmax(0, 1fr) 180px !important;
      align-items: end !important;
      gap: 16px !important;
    }

    [data-bu-print-coping-panels] {
      display: grid !important;
      grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
      align-items: start !important;
      gap: 16px !important;
      padding: 24px !important;
    }

    [data-bu-print-coping-panels] [data-slideshow-target~="slide"] {
      margin-top: 0 !important;
    }

    [data-bu-print-coping-image] {
      display: block !important;
      width: auto !important;
      max-width: 180px !important;
      height: auto !important;
      max-height: 220px !important;
      object-fit: contain !important;
    }

    [data-bu-print-complete-report] {
      display: block !important;
      visibility: visible !important;
      margin-top: 16px !important;
    }

    [data-bu-print-complete-heading] {
      cursor: default !important;
    }

    [data-bu-print-complete-heading] svg {
      display: none !important;
    }

    [data-bu-print-hero] {
      min-height: 0 !important;
      padding: 18px 24px !important;
    }

    [data-bu-print-hero] h3 {
      margin: 0 !important;
      padding: 0 !important;
      font-size: 32px !important;
      line-height: 1.15 !important;
    }

    #bu-print-toc {
      display: none;
    }

    img, svg, canvas {
      max-width: 100% !important;
      height: auto;
    }

    @media (max-width: 700px) {
      [data-bu-print-carousel] .swiper-wrapper,
      [data-bu-print-insight-grid],
      [data-bu-print-coping-panels],
      [data-bu-print-coping-controller] {
        grid-template-columns: 1fr !important;
      }
    }

    @media print {
      body {
        font-size: 10.5pt !important;
      }

      [data-bu-print-hero] h3 {
        font-size: 24pt !important;
      }

      a {
        color: inherit !important;
        text-decoration: none !important;
      }

      #bu-print-toc {
        display: block !important;
        max-width: 170mm;
        margin: 8mm auto 0;
        padding: 8mm 10mm;
        border: 1px solid #c9ddd5;
        border-radius: 8px;
        background: #f6fbf9;
        break-after: page;
        page-break-after: always;
      }

      #bu-print-toc h2 {
        margin: 0 0 4mm;
        font-size: 22pt;
      }

      #bu-print-toc ol {
        margin: 0;
        padding-left: 6mm;
      }

      #bu-print-toc li {
        margin: 0 0 1.6mm;
        break-inside: avoid;
      }

      #bu-print-toc .bu-toc-group {
        break-inside: auto;
        page-break-inside: auto;
      }

      #bu-print-toc .bu-toc-group > a,
      #bu-print-toc .bu-toc-group > span {
        display: block;
        color: #1c3d4a !important;
        font-weight: 700;
        break-after: avoid;
        page-break-after: avoid;
      }

      #bu-print-toc li > ol {
        margin-top: 1.2mm;
        padding-left: 7mm;
        font-size: 9.5pt;
      }

      #bu-print-toc a {
        color: #1c3d4a !important;
        text-decoration: underline !important;
        text-underline-offset: 2px;
      }

      #bu-print-reading-note {
        margin: 0 0 5mm;
        padding: 4mm 5mm;
        border-left: 4px solid #67aa90;
        background: #fff;
        font-size: 9.5pt;
      }

      #bu-print-reading-note h3 {
        margin: 0 0 2mm;
        font-size: 12pt;
      }

      #bu-print-reading-note p {
        margin: 0 0 1.5mm;
      }

      #bu-print-reading-note p:last-child {
        margin-bottom: 0;
      }

      [data-bu-print-carousel] .swiper-wrapper,
      [data-bu-print-insight-grid],
      [data-bu-print-coping-panels] {
        gap: 4mm !important;
      }

      #section-1 [data-bu-print-style="Secondary style"],
      #section-2 [data-bu-print-style="Secondary style"] {
        break-before: page;
        page-break-before: always;
      }

      #section-4 [data-bu-print-style="Secondary style"] {
        break-before: auto;
        page-break-before: auto;
      }

      #section-1,
      #section-2,
      #section-3,
      #section-4,
      #section-5 {
        break-before: page;
        page-break-before: always;
      }

      #section-0,
      #section-3,
      #section-4 {
        break-inside: avoid-page;
        page-break-inside: avoid;
      }

      [data-bu-print-complete-heading] {
        break-after: avoid-page;
        page-break-after: avoid;
      }

      [data-bu-print-complete-report] {
        margin-top: 4mm !important;
      }

      [data-bu-print-major-heading] {
        break-before: page;
        page-break-before: always;
        break-after: avoid;
        page-break-after: avoid;
      }

      [data-bu-print-no-break-before] {
        break-before: auto !important;
        page-break-before: auto !important;
      }

      h1, h2, h3, h4 {
        break-after: avoid;
        page-break-after: avoid;
      }

      p, li {
        orphans: 3;
        widows: 3;
      }

      footer,
      [role="contentinfo"] {
        display: none !important;
      }
    }
  `;

  const style = document.createElement("style");
  style.id = STYLE_ID;
  style.textContent = css;
  document.head.appendChild(style);

  console.info(
    `BlossomUp ${reportTitle} print layout applied. Print with Cmd/Ctrl+P. Reload to restore the page.`,
  );
})();

Notes and Limitations

  • The snippets are intentionally report-specific because the inspected report families use materially different structures.
  • The selectors depend on BlossomUp's current HTML structure and may need updates if the site changes.
  • Each snippet is a one-way transformation of the current page. Reload instead of trying to undo individual changes.
  • Internal contents links may remain clickable in PDF exporters that preserve fragment links, but page JavaScript cannot reliably create native PDF sidebar bookmarks.
  • Browser print engines can still move cards or headings when content is taller than the remaining printable area. Always skim every page of the preview.
  • The code is intended to use standard browser JavaScript and CSS, but only Chrome on macOS has been exercised so far.

Future Testing

  • Re-check the latest Sexual Values Potential Challenges fix in browser and print preview.
  • Run the Conflict Types, BPD, and Narcissist snippets through complete print-preview checks.
  • Test each supported report in Safari.
  • Add separate snippets for materially different report types if useful.
  • Revisit shared helpers only if several more report types reveal a stable common structure.

See Also

Developer-tools documentation

My Other Related Deepdive Gist's and Projects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment