<?xml version="1.0" encoding="UTF-8"?>
<!--
  robots.txt has advertised this file for a long time and it did not exist. The
  request was answered by the Amplify SPA fallback with index.html at HTTP 200
  and Content-Type text/html, so a crawler asking for XML got HTML and treated
  the sitemap as invalid. That is worse than a 404, which is at least honest.

  A real file under public/ is emitted to dist/ and served directly, and the SPA
  fallback only applies to paths with no matching file, so shipping this fixes it
  with no console change.

  ONLY GENUINELY PUBLIC, INDEXABLE ROUTES BELONG HERE. Listing a URL that
  robots.txt disallows, or that is served noindex, is a self-contradicting signal
  and devalues the whole file. So this omits every Disallow-ed route
  (/results, /upload, /post, /quiz, /dashboard, /projects, /settings, /vote/),
  omits /go because it is served X-Robots-Tag: noindex, and omits the account and
  flow routes (/login, /reset-password, /verify-email, /unsubscribe, /team,
  /admin/*) which are functional rather than content.

  No <lastmod>, on purpose. A fabricated or blanket-updated date is a signal
  Google learns to distrust, and nothing here tracks real per-route change dates.
  Omitting it is honest; add it only when something actually knows the answer.

  Add a route here when it becomes public content, and keep it consistent with
  frontend/public/robots.txt. The two files disagreeing is the failure mode.
-->
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://mytopit.com/</loc>
  </url>
  <!--
    /contact, /terms AND /privacy WERE LISTED HERE AND WERE REMOVED ON
    2026-09-15. They are still public, still linked from the footer, and still
    perfectly reachable. They are not listed because of what they SERVE.

    All three are React routes, so Amplify's SPA catch-all answers them with
    index.html: the homepage's <title>, the homepage's description, and
    og:url pointing at the homepage. Home.tsx rewrites the head at runtime, but
    a crawler that does not execute JavaScript never sees that, which is the
    same reason index.html carries no canonical.

    So listing them submitted three URLs that a crawler reads as three more
    copies of the homepage. That is the self-contradicting signal the note above
    describes for Disallow-ed routes, arriving by a different route: not a URL
    that says do not index me, but a URL whose content is not the content the
    sitemap is claiming.

    THEY COME BACK THE DAY THEY GET A PER-ROUTE HEAD IN THE DELIVERED BYTES,
    which means prerendering, which is a separate decision with its own Console
    cost. Re-add them in the same change that ships it, and not before: a
    listing is a claim about what the URL serves, and nothing here changes what
    these three serve.
  -->
  <!--
    /vote/<token> IS DELIBERATELY STILL ABSENT and must stay absent. dist/vote.html
    (scripts/voteHead.mjs) gives it a real head for link-preview bots, but the
    page renders another person's photographs, robots.txt disallows the prefix,
    and the generated head says noindex. Serving a correct preview and listing a
    URL for indexing are different things; this file only does the second.
  -->

  <!--
    The three /guides/ URLs below are PRERENDERED STATIC FILES
    (frontend/public/guides/*.html), not React routes. They need TWO
    independent halves to be reachable, and only one of them lives in this repo.

    HALF ONE, in the repo: the .html files themselves, shipped to dist/ by the
    build. HALF TWO, NOT in the repo: three Amplify Console rewrite rules, one
    per guide, placed ABOVE the SPA catch-all —

      /guides/dating-profile-photos  ->  /guides/dating-profile-photos.html  200
      /guides/linkedin-profile-photo ->  /guides/linkedin-profile-photo.html 200
      /guides/instagram-photo        ->  /guides/instagram-photo.html        200

    BOTH HALVES ARE NOW IN PLACE (2026-08-26, verified by curl, and the rules
    were added by hand in the Console). This note used to be written in the
    future tense because they were not, and the failure it predicted did in fact
    happen: the artifact contained the files and every request still returned the
    3,683-byte SPA shell, because the catch-all's extension allowlist has no
    `html` entry and its first branch is `^[^.]+$`, so BOTH the extensionless
    path and the .html path fell through to it.

    Measured after the rules landed: dating 9,016 bytes, linkedin 8,142,
    instagram 6,411 — real content, stable etags across repeated fetches.

    WHY THE TWO HALVES ARE WORTH SPELLING OUT: a Console rule is invisible from
    this repository. Nothing in a diff, a review or CI can tell you it is gone,
    and its removal produces no error — just three pages quietly serving the
    homepage at HTTP 200 while this file keeps advertising them. That is the
    same class of self-contradicting signal the note above describes for
    disallowed routes.

    So if the Console rules are ever removed, remove these three entries in the
    same change. Check with curl, not a browser:
      curl -s https://mytopit.com/guides/dating-profile-photos | head -c 200
    A body starting `<!doctype html>` at the homepage byte count means the
    rewrite is gone.
  -->
  <url>
    <loc>https://mytopit.com/guides/dating-profile-photos</loc>
  </url>
  <url>
    <loc>https://mytopit.com/guides/linkedin-profile-photo</loc>
  </url>
  <url>
    <loc>https://mytopit.com/guides/instagram-photo</loc>
  </url>
</urlset>
