const { useMemo, useState } = React;

const CLUSTER_VERSION = "etsy-limit-cluster-1";
const STRUCTURED_DATA_TYPE = "FAQPage";

function splitTags(input) {
  return input
    .split(/[\n,]+/)
    .map((tag) => tag.trim())
    .filter(Boolean);
}

const PAGES = {
  "/etsy-tag-character-limit": {
    eyebrow: "Etsy tag character limit · 2026",
    title: "Etsy Tag Character Limit: 13 Tags, 20 Characters Each",
    lead: "13 slots. 20 characters each. Spaces count. Here's exactly how to use them without wasting coverage.",
    toolTitle: "Tag Checker",
    primaryCta: { href: "/etsy-tag-generator", label: "Generate better Etsy tags" },
    secondaryCta: { href: "/etsy-listing-generator", label: "Open listing workspace" },
    facts: [
      ["Tag slots", "13", "Per listing — use all of them"],
      ["Per tag", "20 characters", "Spaces count toward this"],
      ["One-word tags", "Rarely worth it", "Phrases convert better"],
      ["Title overlap", "Some is fine", "Tags should add coverage, not repeat"],
    ],
    quickAnswer:
      "Etsy allows 13 tags per listing. Each tag can be up to 20 characters, including spaces. Multi-word phrases are allowed — \"dog mom mug\" is 11 characters and counts as one tag.",
    references: [
      ["Official Etsy rule", "Etsy Help: listings can use up to 13 tags, each up to 20 characters.", "https://help.etsy.com/hc/en-gb/articles/360000336307-How-to-Use-Tags-to-Get-Found-in-Search", "official"],
      ["Market guidance", "SEO guides treat empty tag slots as unused search coverage — most recommend filling all 13 with distinct phrases.", "https://www.listing-forge.com/blog/etsy-character-limits", "guide"],
      ["ShopFoundry recommendation", "Fill all 13 slots. Use phrases, not single words. Don't pad to hit 20 characters — a shorter accurate tag beats a padded vague one.", "/etsy-tag-generator", "recommendation"],
    ],
    limits: [
      ["Tags per listing", "13", "Each unused slot is a search signal you're leaving blank."],
      ["Characters per tag", "20 (spaces count)", "\"custom dog portrait\" = 19 chars — fits. \"personalized dog portrait\" = 25 chars — doesn't."],
      ["Phrases allowed?", "Yes", "\"cat mom gift\" counts as one tag and carries more search intent than \"cat\" alone."],
      ["Punctuation", "Letters, numbers, spaces only", "Hyphens and special characters are not supported in tags."],
    ],
    sources: [
      ["Official Etsy tags help", "https://help.etsy.com/hc/en-gb/articles/360000336307-How-to-Use-Tags-to-Get-Found-in-Search"],
      ["Etsy character limits reference", "https://www.listing-forge.com/blog/etsy-character-limits"],
      ["Tag strategy guide", "https://www.etsyedge.com/blog/how-to-use-etsy-tags"],
    ],
    sections: [
      ["Do spaces count toward the 20-character limit?", "Yes. Every space counts. \"dog mom mug\" is 11 characters (9 letters + 2 spaces). That's well within the limit. \"personalized coffee mug\" is 22 characters — two over, so Etsy won't accept it."],
      ["Should tags be phrases or single words?", "Phrases. A single-word tag like \"mug\" competes with every mug listing on Etsy. A phrase like \"custom dog coffee mug\" targets exactly what a buyer typed. Use single words only when no useful phrase fits."],
      ["Should all 13 tag slots be used?", "Yes, if you can fill them with accurate phrases. Each slot is a separate chance to match a buyer search. Leaving slots empty doesn't help — there's no penalty for using all 13, only for using irrelevant ones."],
      ["How much should tags overlap with the title?", "Some overlap is fine — Etsy uses both fields for search matching. But tags should expand coverage, not just repeat the title. If the title says \"leather wallet men,\" use tags for style (slim, bifold, minimalist), material alternatives (vegan leather), occasions, and recipients."],
      ["What's the right mix of tag types for one listing?", "A strong 13-tag set typically covers: 3–4 product phrases (what it is, variants, materials), 2–3 audience or recipient phrases (who it's for), 2–3 occasion tags (when it's bought), 1–2 style or attribute tags, and 1–2 broader category phrases as backup."],
    ],
    examples: [
      ["Weak", "leather bag, leather purse, leather crossbody, crossbody bag, women bag, brown bag, bag gift", "Seven of thirteen slots saying the same thing. Buyers searching for any variant see the same listing — no extra coverage gained."],
      ["Better", "vegan leather crossbody, small shoulder bag, everyday work bag, minimalist purse, boho bag women, gift for her birthday, brown leather look, convertible strap bag", "Eight distinct buyer searches covered. More angles, same character budget."],
    ],
    mistakes: [
      ["Leaving tag slots empty.", "There's no downside to using all 13. Every empty slot is a search you won't show up for."],
      ["Repeating the title word-for-word across multiple tags.", "Tags that copy the title phrase don't add coverage — they just repeat what Etsy already has from the title field."],
      ["Writing tags longer than 20 characters.", "Etsy rejects them silently or truncates. Check length before publishing — \"personalized dog portrait\" (25 chars) needs to become \"custom dog portrait\" (18 chars)."],
      ["Using only one-word tags.", "\"necklace,\" \"gift,\" \"silver\" — all too broad. Buyers search for phrases. Match what they actually type."],
      ["Stuffing the same niche keyword into 6 different tags.", "Pick your best 2 versions of that phrase and use the remaining slots for different angles entirely."],
    ],
    faqs: [
      ["What is the Etsy tag character limit?", "Each Etsy tag can be up to 20 characters, including spaces. You get 13 tags per listing."],
      ["Do spaces count in Etsy tags?", "Yes. \"cat mom gift\" is 12 characters (10 letters + 2 spaces) and counts as one tag within the 20-character limit."],
      ["Can Etsy tags be multi-word phrases?", "Yes. Phrases are generally better than single words because they match specific buyer searches. \"minimalist gold ring\" (19 chars) outperforms \"ring\" for intent."],
      ["What happens if a tag is over 20 characters?", "Etsy will not accept it. You'll need to shorten the phrase — drop a word, use an abbreviation, or find a tighter synonym."],
      ["Should I use all 13 Etsy tags?", "Yes, as long as every tag is accurate. Empty slots are missed search coverage. Filling them with irrelevant phrases is worse than leaving them empty — but accurate phrases always help."],
      ["Can Etsy tags use hyphens or special characters?", "No. Etsy tags support letters, numbers, and spaces only. Hyphens and other punctuation are not allowed."],
      ["Should tags match my title exactly?", "Not exactly — some word overlap is normal, but tags should cover buyer searches the title doesn't. Think: synonyms, styles, materials, occasions, recipients."],
    ],
    related: [
      ["/etsy-tag-generator", "Etsy Tag Generator"],
      ["/etsy-title-character-limit", "Etsy Title Character Limit"],
      ["/etsy-description-character-limit", "Etsy Description Character Limit"],
    ],
    relatedTitle: "Related tag and listing tools",
    relatedIntro: "Once the rules are clear, use these to build a tag set that covers the right search angles.",
    tool: "tags",
  },
  "/etsy-description-character-limit": {
    eyebrow: "Etsy description character limit · 2026",
    title: "Etsy Description Character Limit: What the First 160 Characters Actually Do",
    lead: "Etsy hasn't published a single hard cap. What actually matters is what buyers see before they click — the opening 160 characters.",
    toolTitle: "Description Checker",
    primaryCta: { href: "/etsy-description-generator", label: "Generate an Etsy description" },
    secondaryCta: { href: "/etsy-listing-generator", label: "Open listing workspace" },
    facts: [
      ["Published hard cap", "None confirmed", "Etsy guidance focuses on content quality"],
      ["First 160 characters", "The real target", "What buyers see in search and on mobile"],
      ["Opening line rule", "Product first", "Skip the shop greeting — lead with what it is"],
      ["Format", "Plain text only", "Line breaks work; HTML and Markdown don't"],
    ],
    quickAnswer:
      "Etsy has not published a confirmed character limit for listing descriptions. Third-party guides cite different numbers. The practical rule is: make the first 160 characters count — that's what buyers see in search results and mobile previews.",
    references: [
      ["Official Etsy rule", "Etsy Help says to describe item details clearly and use relevant keywords naturally — no hard character number is given.", "https://help.etsy.com/hc/en-us/articles/115015628707-How-to-Create-a-Listing", "official"],
      ["Market guidance", "Third-party guides cite different hard cap estimates (ranging from 13,000 to no stated cap). ShopFoundry does not claim any of them as official.", "https://lettercounter.org/blog/etsy-listing-character-limits/", "guide"],
      ["ShopFoundry recommendation", "Write the first 160 characters like a product pitch: item type, key detail, personalization, buyer use case. Then use bullets for dimensions, care, and shipping.", "/etsy-description-generator", "recommendation"],
    ],
    limits: [
      ["Official character cap", "Not confirmed", "Etsy guidance focuses on clarity over length. Don't pad for the sake of hitting a number."],
      ["First 160 characters", "Treat as your preview", "This is what appears in search results and mobile listing cards. Lead with the product."],
      ["Opening line", "No greetings", "\"Welcome to my shop\" wastes the preview on words buyers don't need."],
      ["Format", "Plain text", "Etsy renders line breaks. Bullet points using • or - are readable. HTML tags are not."],
    ],
    sources: [
      ["Official Etsy listing help", "https://help.etsy.com/hc/en-us/articles/115015628707-How-to-Create-a-Listing"],
      ["Character limits reference", "https://www.listing-forge.com/blog/etsy-character-limits"],
      ["Description length guide", "https://lettercounter.org/blog/etsy-listing-character-limits/"],
    ],
    sections: [
      ["Is there an official Etsy description character limit?", "No confirmed number has been published by Etsy. Various third-party guides cite different figures, none of which Etsy has verified. Write as much as buyers need to make a confident purchase decision — and no more."],
      ["Why do the first 160 characters matter so much?", "That's the portion that shows up in Etsy search result snippets and on mobile listing cards before a buyer taps through. If that space says \"Welcome to my shop! I hope you love this item,\" buyers see nothing useful before deciding whether to click."],
      ["What should the opening line actually say?", "State the product type, its most important feature, and who it's for — all in one sentence. Example: \"Personalized leather journal with hand-stamped initials, refillable lined pages, and vintage cover — made for writers, travelers, and graduation gifts.\" That's 154 characters and answers the buyer's first question immediately."],
      ["How should the rest of the description be structured?", "After the opening pitch: use short bullets for specs (dimensions, materials, weight), a personalization section if relevant (what to include at checkout), turnaround time, shipping notes, and care instructions. Buyers on mobile scan — walls of text get skipped."],
      ["Does description content help with Etsy search?", "Etsy's guidance says to include relevant keywords naturally in listing content. Description content is a secondary signal compared to title and tags, but it still supports matching. More importantly, a clear description reduces hesitation and improves conversion once a buyer lands on the listing."],
    ],
    examples: [
      ["Weak opening", "Welcome to Maple & Thread! 🌿 We're a small shop making handmade goods with love. This item is carefully crafted and we hope it brings joy!", "73 characters in and still no product information. Buyers who scan search results learn nothing."],
      ["Strong opening", "Personalized leather journal — hand-stamped initials, refillable lined pages, soft vintage cover. For writers, travelers, and graduation gifts. Ships in 3–5 days.", "Covers product, customization, use case, and shipping expectation in 162 characters."],
    ],
    mistakes: [
      ["Opening with a shop greeting or emoji string.", "The first sentence is preview real estate. Use it to describe what the buyer gets, not to introduce your brand."],
      ["Copying tag phrases into the description as a keyword list.", "\"leather journal personalized custom gift handmade\" is not a sentence. It reads as spam to buyers and adds no SEO value Etsy doesn't already get from the tag field."],
      ["Writing one long unbroken paragraph.", "Mobile buyers don't read walls of text. Break into short sections: opening pitch → specs → personalization → shipping → care."],
      ["Leaving out purchase-critical details.", "Size, material, what's included, personalization instructions, and turnaround time are the questions buyers have before purchasing. Answer them in the description."],
      ["Burying the product type halfway down.", "If \"ceramic mug\" appears in sentence four, mobile buyers may bounce before reaching it. Product type belongs in sentence one."],
    ],
    faqs: [
      ["What is the Etsy description character limit?", "Etsy has not published a confirmed hard limit for listing descriptions. Focus on the first 160 characters — that's the preview buyers see before clicking."],
      ["Do the first 160 characters actually matter?", "Yes. They appear in Etsy search result snippets and on mobile listing cards. Starting with a shop greeting wastes the space buyers use to decide whether to click."],
      ["Does the Etsy description help with SEO?", "It's a secondary search signal after title and tags. It matters more for buyer conversion — a clear description reduces hesitation once someone lands on the listing."],
      ["Should Etsy descriptions be long or short?", "Long enough to answer every pre-purchase question: what it is, what it's made of, sizing, personalization instructions, turnaround, and care. Short enough that buyers on mobile don't give up halfway."],
      ["Can I use HTML formatting in Etsy descriptions?", "No. Etsy descriptions render as plain text. Use line breaks and simple bullet characters (• or -) for scannable structure."],
      ["Should I include keywords in the description?", "Use keywords where they fit naturally inside useful buyer information. Don't paste a tag list as a sentence — it helps neither buyers nor search."],
    ],
    related: [
      ["/etsy-description-generator", "Etsy Description Generator"],
      ["/etsy-title-character-limit", "Etsy Title Character Limit"],
      ["/etsy-tag-character-limit", "Etsy Tag Character Limit"],
    ],
    relatedTitle: "Related description and listing tools",
    relatedIntro: "A strong description works best when the title and tags are already covering the same product from the right angles.",
    tool: "description",
  },
  "/etsy-t-shirt-title-character-limit": {
    eyebrow: "Etsy t-shirt title character limit · 2026",
    title: "Etsy T-Shirt Title Character Limit: 140 Characters, One Apparel Term",
    lead: "Same 140-character limit as every Etsy listing. The difference is shirt titles go spammy faster — here's the structure that keeps them clean.",
    toolTitle: "T-Shirt Title Checker",
    primaryCta: { href: "/etsy-title-generator", label: "Generate Etsy titles" },
    secondaryCta: { href: "/etsy-listing-generator", label: "Open listing workspace" },
    facts: [
      ["Title limit", "140 characters", "No separate limit for shirts or POD"],
      ["Visible on mobile", "First ~40 chars", "Design and product type go first"],
      ["Apparel terms", "1–2 max", "shirt + tee + t-shirt + top = keyword stuffing"],
      ["The rest", "Goes in tags", "Synonyms, holidays, audiences, gift phrases"],
    ],
    quickAnswer:
      "Etsy t-shirt titles use the same 140-character limit as all Etsy listings. There is no separate limit for shirts or POD products. The challenge is that POD sellers tend to stack apparel synonyms (shirt, tee, t-shirt, top, graphic tee) in a single title, which burns characters and looks like spam.",
    references: [
      ["Official Etsy rule", "Etsy's title field is 140 characters for all listing types. Current seller guidance emphasizes readable titles over keyword strings.", "https://www.etsy.com/seller-handbook/article/1399426136697", "official"],
      ["Market guidance", "POD-specific title guides focus on three things: put the design noun first, use one apparel term, and move synonyms and audience phrases into tags.", "https://www.s27pod.com/blog/etsy-title-optimization.html", "guide"],
      ["ShopFoundry recommendation", "One apparel term in the title. Front-load the design or niche. Everything else — synonym variants, holidays, audiences — belongs in tags.", "/etsy-title-generator", "recommendation"],
    ],
    limits: [
      ["Title character limit", "140 characters", "Identical to every other Etsy listing type — no POD exception."],
      ["Mobile visible portion", "~40 characters", "On most phones, only the first 35–45 characters show before truncation. \"Funny Cat Mom Shirt\" fits. \"Funny Cat Shirt Cat Tee Cat T-Shirt\" doesn't."],
      ["Apparel terms per title", "1–2 max", "shirt, tee, t-shirt, top, and graphic tee all mean the same thing to buyers — pick one."],
      ["Size and color", "Use attributes", "Don't put size or color in the title. Etsy's listing attributes handle variations more cleanly."],
    ],
    titleVsTags: [
      ["Title", "Design/theme + one apparel term + main audience or occasion. Keep it under 80 characters if possible."],
      ["Tags", "Apparel synonyms (tee, tshirt, top), holiday variants, niche interest phrases, recipient terms, and gift occasion keywords."],
      ["Attributes", "Size, color, sleeve type, fit (relaxed, slim), and print placement. These don't belong in the title."],
    ],
    sources: [
      ["Official Etsy title guidance", "https://www.etsy.com/seller-handbook/article/1399426136697"],
      ["POD title optimization guide", "https://www.s27pod.com/blog/etsy-title-optimization.html"],
      ["T-shirt listing SEO guide", "https://www.insightagent.app/guides/etsy-seo-for-t-shirt-listings"],
    ],
    sections: [
      ["Does Etsy have a separate character limit for t-shirt titles?", "No. All Etsy listing titles share the same 140-character limit regardless of product type. There is no special limit for shirts, POD products, or apparel."],
      ["Why do shirt titles get spammy so fast?", "Most POD sellers list the same product under multiple apparel terms — shirt, tee, t-shirt, graphic tee, top — all in one title. That eats 50+ characters saying the same thing five different ways. Buyers scanning search results see a repetitive string instead of a clear product identity."],
      ["What's the right structure for a T-shirt title?", "Design/niche + one apparel term + audience or occasion. Example: \"Funny Cat Mom Shirt, Cozy Graphic Tee for Women, Pet Lover Gift\" — 62 characters, readable, and leaves room for search context. The \"graphic tee\" in the middle is the only second apparel reference, and it's natural rather than stacked."],
      ["What about seasonal and gift-occasion keywords?", "Put one strong occasion in the title if it's central to the design (e.g., Mother's Day). Move extra occasions — Christmas, Valentine's Day, Teacher Appreciation — into tags. Titles don't need to chase every holiday; tags can handle the calendar rotation."],
      ["Should POD sellers use all 140 characters?", "Not necessarily. A clear 70-character title often outperforms a padded 140-character one because mobile buyers scan the opening and move on. Front-load what matters, then stop. The rest of the character budget is available if you need it — not a quota to fill."],
    ],
    examples: [
      ["Weak", "Funny Cat Shirt Cat Tee Cat T-Shirt Cat Lover Shirt Graphic Tee Women Shirt Cat Gift Shirt", "88 characters spent on apparel synonyms and repetition. A buyer sees \"cat shirt\" six different ways before getting to any useful detail."],
      ["Better", "Funny Cat Mom Shirt — Cozy Graphic Tee for Women, Pet Lover Gift", "64 characters. Design is clear, one apparel term leads, audience and occasion follow. Tags can cover \"cat tee,\" \"cat t-shirt,\" and seasonal variants."],
    ],
    mistakes: [
      ["Stacking shirt, tee, t-shirt, and top in the same title.", "Pick the one term buyers use most for your niche and put the variants in tags. Most buyers search for one phrase, not all four."],
      ["Putting the design after a string of product-type words.", "\"Shirt Tee Graphic Tee Women Funny Cat Mom\" — the actual product identity is buried. Flip it: design first, product type second."],
      ["Including size or color in the title.", "Etsy provides listing variations for a reason. Size (S, M, L) and color in the title waste characters and don't affect search matching the way attributes do."],
      ["Treating 140 characters as a minimum.", "Shorter and clearer usually wins on mobile. If the title is complete at 75 characters, publish it at 75."],
      ["Adding a holiday to every title.", "A Christmas shirt title is fine in November. In April it looks stale and confuses buyers. Put holiday variants in tags — they're easier to manage."],
    ],
    faqs: [
      ["What is the Etsy t-shirt title character limit?", "140 characters — the same limit as every other Etsy listing. There is no separate limit for t-shirts or POD products."],
      ["Should I include shirt, tee, and t-shirt in the same title?", "No. Pick one and put the others in tags. Stacking apparel synonyms wastes characters and makes titles harder to read."],
      ["How many characters are visible before mobile truncation?", "Roughly 35–45 characters on most phones. Put the design identity and product type in that window."],
      ["Where should holiday or occasion keywords go?", "One strong occasion can go in the title if the design is specifically for that holiday. Additional occasions belong in tags so you can update them seasonally."],
      ["Should I use all 140 characters for a shirt title?", "Only if you need them. A focused 65-character title is better than a padded 140-character one. Mobile buyers see the opening — fill it with the design and product type, not synonyms."],
      ["Can I put size and color in the Etsy title?", "You can, but it's not recommended. Use Etsy's listing variations for size and color. Titles are for product identity and buyer search phrases, not inventory details."],
    ],
    related: [
      ["/etsy-title-character-limit", "Etsy Title Character Limit"],
      ["/etsy-tag-character-limit", "Etsy Tag Character Limit"],
      ["/etsy-title-generator", "Etsy Title Generator"],
    ],
    relatedTitle: "T-shirt title next steps",
    relatedIntro: "Get the character budget right, then build a title that front-loads the design and leaves the keyword variants for tags.",
    tool: "tshirt",
  },
};

const DEFAULT_PATH = "/etsy-tag-character-limit";

function getPage() {
  const path = window.location.pathname.replace(/\/$/, "");
  return PAGES[path] || PAGES[DEFAULT_PATH];
}

function Nav() {
  const [openMenu, setOpenMenu] = useState(null);
  const toggle = (id) => setOpenMenu(openMenu === id ? null : id);

  const menuStyle = {
    position: "absolute", top: "calc(100% + 8px)", left: "50%", transform: "translateX(-50%)",
    background: "rgba(253,252,248,0.98)", border: "1.5px solid rgba(222,216,207,0.8)",
    borderRadius: 16, boxShadow: "0 12px 36px -8px rgba(93,112,82,0.18)",
    padding: 6, minWidth: 230, zIndex: 200,
  };
  const itemStyle = {
    display: "flex", alignItems: "center", gap: 10,
    padding: "10px 12px", borderRadius: 10,
    color: "#2C2C24", fontWeight: 700, fontSize: 14, textDecoration: "none",
  };
  const iconStyle = {
    width: 28, height: 28, borderRadius: 8,
    background: "rgba(93,112,82,0.1)",
    display: "flex", alignItems: "center", justifyContent: "center",
    fontSize: 13, flexShrink: 0,
  };

  return (
    <header style={{ position: "sticky", top: 0, zIndex: 50, background: "rgba(253,252,248,0.95)", backdropFilter: "blur(12px)", borderBottom: "1px solid rgba(222,216,207,0.6)" }}>
      <nav style={{ maxWidth: 1100, margin: "0 auto", padding: "0 24px", height: 56, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <a href="/" style={{ display: "flex", alignItems: "center", gap: 9, textDecoration: "none" }}>
          <img src="/logo.png" alt="ShopFoundry" style={{ width: 28, height: 28, borderRadius: 6 }} />
          <span style={{ fontFamily: "'Fraunces',serif", fontWeight: 700, fontSize: 17, color: "#2C2C24" }}>ShopFoundry</span>
        </a>
        <div style={{ display: "flex", alignItems: "center", gap: 2 }}>
          <a href="/listing-studio" style={navLinkStyle}>Listing Studio</a>

          {/* Write a Listing dropdown */}
          <div style={{ position: "relative" }}>
            <button onClick={() => toggle('write')} style={{ ...navLinkStyle, border: 0, background: "transparent", cursor: "pointer", display: "flex", alignItems: "center", gap: 4 }}>
              Write a Listing
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" style={{ transition: "transform 0.2s", transform: openMenu === 'write' ? "rotate(180deg)" : "" }}><path d="M2 4l4 4 4-4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
            {openMenu === 'write' && (
              <div style={menuStyle}>
                {[
                  ["/etsy-listing-generator", "✍️", "Listing Generator"],
                  ["/etsy-title-generator", "✍️", "Title Generator"],
                  ["/etsy-tag-generator", "🏷️", "Tag Generator"],
                  ["/etsy-description-generator", "📄", "Description Generator"],
                ].map(([href, emoji, label]) => (
                  <a key={`${href}-${label}`} href={href} style={itemStyle}>
                    <span style={iconStyle}>{emoji}</span>{label}
                  </a>
                ))}
              </div>
            )}
          </div>

          {/* Fix a Listing dropdown */}
          <div style={{ position: "relative" }}>
            <button onClick={() => toggle('fix')} style={{ ...navLinkStyle, border: 0, background: "transparent", cursor: "pointer", display: "flex", alignItems: "center", gap: 4 }}>
              Fix a Listing
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" style={{ transition: "transform 0.2s", transform: openMenu === 'fix' ? "rotate(180deg)" : "" }}><path d="M2 4l4 4 4-4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
            {openMenu === 'fix' && (
              <div style={menuStyle}>
                {[
                  ["/etsy-listing-analyzer", "🔎", "Listing Analyzer"],
                  ["/etsy-listing-analyzer", "⚡", "Optimize Existing Listing"],
                  ["/etsy-title-character-limit", "📏", "Character Limits"],
                  ["/etsy-title-character-limit", "140", "Title Limit"],
                  ["/etsy-tag-character-limit", "13", "Tag Limit"],
                  ["/etsy-description-character-limit", "160", "Description Limit"],
                  ["/etsy-t-shirt-title-character-limit", "T", "T-Shirt Title Limit"],
                ].map(([href, emoji, label]) => (
                  <a key={href} href={href} style={itemStyle}>
                    <span style={iconStyle}>{emoji}</span>{label}
                  </a>
                ))}
              </div>
            )}
          </div>

          <a href="/pricing" style={navLinkStyle}>Pricing</a>
        </div>
        <a href="/etsy-listing-generator" style={{ background: "#5D7052", color: "#F3F4F1", borderRadius: 999, fontWeight: 800, padding: "8px 20px", fontSize: 14, textDecoration: "none", whiteSpace: "nowrap" }}>Start free</a>
      </nav>
    </header>
  );
}

const navLinkStyle = {
  padding: "7px 14px",
  borderRadius: 999,
  fontSize: 14.5,
  fontWeight: 700,
  color: "#78786C",
  textDecoration: "none",
  fontFamily: "'Nunito',sans-serif",
};

function Hero({ page }) {
  return (
    <section className="hero" id="checker">
      <span className="blob moss b-1 b1" />
      <span className="blob clay b-2 b2" />
      <div className="wrap hero-grid">
        <div className="hero-copy">
          <span className="eyebrow"><span className="dot" /> {page.eyebrow}</span>
          <h1 className="h1" style={{ marginTop: 20 }}>{page.title}</h1>
          <p className="lead hero-sub">{page.lead}</p>
          <div className="summary-cards">
            {page.facts.map(([label, value, sub]) => (
              <div className="summary-card" key={label}>
                <span className="label">{label}</span>
                <span className="value">{value}</span>
                <span className="sub">{sub}</span>
              </div>
            ))}
          </div>
          <div className="hero-meta">
            <span className="check">Current limits first</span>
            <span className="check">Examples before you publish</span>
            <span className="check">Checker updates as you type</span>
          </div>
        </div>
        <div className="analyzer" style={{ width: "100%", maxWidth: 920 }}>
          <div className="analyzer-head">
            <div className="title"><span className="live-dot" /> {page.toolTitle}</div>
          </div>
          <div className="analyzer-body">
            {page.tool === "tags" && <TagChecker />}
            {page.tool === "description" && <DescriptionChecker />}
            {page.tool === "tshirt" && <TshirtTitleChecker />}
            <a className="ai-trigger workspace-trigger" href={page.primaryCta.href}>
              <span className="workspace-trigger-copy">
                <strong>{page.primaryCta.label}</strong>
                <small>Use ShopFoundry to turn the limit check into usable Etsy listing copy.</small>
              </span>
              <span className="workspace-cta">Open tool</span>
            </a>
          </div>
        </div>
      </div>
    </section>
  );
}

function TagChecker() {
  const [text, setText] = useState("dog mom mug\npet portrait\ncustom dog gift\nceramic mug\ndog lover gift\nstoneware mug\nbirthday for her");
  const tags = useMemo(() => splitTags(text), [text]);
  const duplicates = new Set(tags.map((t) => t.toLowerCase()).filter((tag, index, arr) => arr.indexOf(tag) !== index));
  const over = tags.filter((tag) => tag.length > 20);
  return (
    <div className="tag-editor">
      <div className="counter-row">
        <span className="counter-label">Etsy tags — 13 max · 20 characters each</span>
        <span className={`counter ${tags.length > 13 || over.length ? "bad" : tags.length >= 10 ? "good" : "warn"}`}><span className="num">{tags.length}</span> / 13 tags · {Math.max(0, 13 - tags.length)} slots left</span>
      </div>
      <textarea className="input" value={text} onChange={(event) => setText(event.target.value)} placeholder="Paste tags, one per line..." />
      <div className="tag-chips">
        {tags.map((tag, index) => (
          <span key={`${tag}-${index}`} className={`tag-chip ${tag.length > 20 || duplicates.has(tag.toLowerCase()) ? "bad" : tag.length >= 18 ? "warn" : ""}`}>
            <span>{tag}</span><span className="count mono">{tag.length}/20</span>
          </span>
        ))}
        {Array.from({ length: Math.max(0, 13 - tags.length) }).map((_, index) => <span key={index} className="tag-chip" style={{ background: "transparent", borderStyle: "dashed", color: "var(--fg-4)" }}>slot {tags.length + index + 1}</span>)}
      </div>
      <InlineChecks rows={[
        [tags.length <= 13, `${tags.length} tags entered; Etsy allows up to 13.`],
        [over.length === 0, over.length ? `${over.length} tag(s) are over 20 characters.` : "Every tag is within 20 characters."],
        [duplicates.size === 0, duplicates.size ? "Duplicate tags detected; use the space for new search angles." : "No exact duplicate tags detected."],
      ]} />
    </div>
  );
}

function DescriptionChecker() {
  const [text, setText] = useState("Personalized leather journal with hand-stamped initials, refillable pages, and a soft vintage cover for writers, travelers, and graduation gifts.\n\nDetails\n- Refillable lined pages\n- Soft brown cover\n- Gift-ready packaging");
  const first160 = text.slice(0, 160);
  return (
    <div>
      <div className="counter-row">
        <span className="counter-label">Etsy description draft · first 160-character preview</span>
        <span className="counter good"><span className="num">{text.length}</span> characters</span>
      </div>
      <textarea className="input tall" value={text} onChange={(event) => setText(event.target.value)} placeholder="Paste your Etsy description..." />
      <div className="previews">
        <Preview title="Opening Preview" subtitle="How buyers may see the opening of your description.">{first160}{text.length > 160 ? "..." : ""}</Preview>
      </div>
    </div>
  );
}

function TshirtTitleChecker() {
  const [text, setText] = useState("Funny Cat Mom Shirt, Cozy Graphic Tee for Women, Pet Lover Gift");
  const remaining = 140 - text.length;
  const first40 = text.slice(0, 40);
  return (
    <div>
      <div className="counter-row">
        <span className="counter-label">Etsy T-shirt title — up to 140 characters</span>
        <span className={`counter ${text.length > 140 ? "bad" : text.length > 110 ? "warn" : "good"}`}><span className="num">{text.length}</span> / 140 chars · {remaining >= 0 ? `${remaining} remaining` : `${Math.abs(remaining)} over`}</span>
      </div>
      <textarea className="input" value={text} maxLength={180} onChange={(event) => setText(event.target.value)} placeholder="Paste your T-shirt title..." />
      <div className={`bar ${text.length > 140 ? "bad" : text.length > 110 ? "warn" : "good"}`}><span style={{ width: `${Math.min(100, (text.length / 140) * 100)}%` }} /></div>
      <div className="previews">
        <Preview title="Opening Preview" subtitle="How the start of your Etsy title may appear before it gets cut off.">{first40 || "Start typing to preview the opening."}</Preview>
      </div>
    </div>
  );
}

function Preview({ title, subtitle, children }) {
  return (
    <div className="preview-card">
      <div className="ph"><span className="pdot" /> {title}</div>
      {subtitle && <p className="preview-note">{subtitle}</p>}
      <div className="body">{children}</div>
    </div>
  );
}

function InlineChecks({ rows }) {
  return (
    <div className="analysis">
      <div className="analysis-group">
        <div className="ag-head">
          <div className="lhs">
            <span className="status-dot status-good" />
            <span className="name">Live checks</span>
            <span className="summary">Guidance updates as you type</span>
          </div>
        </div>
        <div className="ag-list">
          {rows.map(([pass, text], index) => <div className={`ag-row ${pass ? "good" : "bad"}`} key={index}><span className="d" /><span>{text}</span></div>)}
        </div>
      </div>
    </div>
  );
}

function ContentSections({ page }) {
  return (
    <div className="page-body">
      <div className="wrap">
        <main className="page-main" style={{ maxWidth: 880, margin: "0 auto" }}>
          <section className="section" id="quick-answer">
            <div className="section-head">
              <span className="eyebrow"><span className="dot" /> Quick answer</span>
              <h2 className="h2">The short answer.</h2>
              <p className="lead">{page.quickAnswer}</p>
            </div>
            <div className="qa-list">
              {page.references.map(([badge, text, href, tone], index) => (
                <div className={`qa-item ${index === 0 ? "highlight" : ""}`} key={badge}>
                  <span className={`evidence-badge ${tone || "neutral"}`}>{badge}</span>
                  <p className="ans">{text}</p>
                  <a href={href} className="strat" style={{ color: "var(--primary-deep)", fontWeight: 800 }}>Reference source</a>
                </div>
              ))}
            </div>
          </section>
          <section className="section" id="quick-limits">
            <div className="section-head">
              <span className="eyebrow"><span className="dot" /> Quick limits table</span>
              <h2 className="h2">The numbers to check first.</h2>
            </div>
            <div className="limits-table">
              <div className="limits-row limits-head">
                <span>Field</span>
                <span>Limit</span>
                <span>What to know</span>
              </div>
              {page.limits.map(([field, limit, note]) => (
                <div className="limits-row" key={field}>
                  <span>{field}</span>
                  <strong>{limit}</strong>
                  <span>{note}</span>
                </div>
              ))}
            </div>
          </section>
          {page.titleVsTags && (
            <section className="section" id="title-vs-tags">
              <div className="section-head">
                <span className="eyebrow"><span className="dot" /> Title vs. tags</span>
                <h2 className="h2">Where each search phrase belongs.</h2>
              </div>
              <div className="qa-list">
                {page.titleVsTags.map(([label, guidance]) => (
                  <div className="qa-item" key={label}>
                    <span className="qmark">{label}</span>
                    <p className="ans">{guidance}</p>
                  </div>
                ))}
              </div>
            </section>
          )}
          <section className="section" id="details">
            <div className="section-head">
              <span className="eyebrow"><span className="dot" /> Rules and recommendations</span>
              <h2 className="h2">What sellers usually need to know.</h2>
            </div>
            <div className="qa-list">
              {page.sections.map(([question, answer], index) => (
                <div className={index === 0 ? "qa-item highlight" : "qa-item"} key={question}>
                  <span className="qmark"><span className="num">Q{index + 1}</span> {question}</span>
                  <h3>{question}</h3>
                  <p className="ans">{answer}</p>
                </div>
              ))}
            </div>
          </section>
          <section className="section" id="examples">
            <div className="section-head">
              <span className="eyebrow"><span className="dot" /> Examples</span>
              <h2 className="h2">Weak vs. better.</h2>
            </div>
            <div className="qa-list">
              {page.examples.map(([label, text, why]) => (
                <div className="qa-item" key={label}>
                  <div className="ex-rows">
                    <div className={`ex-row ${label === "Weak" ? "bad" : "good"}`}><span className="lbl">{label}</span><span className="txt">{text}</span></div>
                  </div>
                  <p className="strat">{why}</p>
                </div>
              ))}
            </div>
          </section>
          <section className="section" id="mistakes">
            <div className="section-head">
              <span className="eyebrow"><span className="dot" /> Common mistakes</span>
              <h2 className="h2">What to avoid.</h2>
            </div>
            <div className="mistakes-list">
              {page.mistakes.map(([wrong, fix], index) => (
                <div className="mistake-row" key={wrong}>
                  <span className="no">{String(index + 1).padStart(2, "0")}</span>
                  <div><h3>{wrong}</h3><div className="instead"><strong>Do this instead</strong>{fix}</div></div>
                </div>
              ))}
            </div>
          </section>
          <FAQ faqs={page.faqs} />
          <Sources sources={page.sources} />
          <Related page={page} />
        </main>
      </div>
    </div>
  );
}

function Sources({ sources }) {
  return (
    <section className="section" id="sources">
      <div className="section-head">
        <span className="eyebrow"><span className="dot" /> Sources</span>
        <h2 className="h2">What this page is based on.</h2>
      </div>
      <div className="qa-list">
        {sources.map(([label, href]) => (
          <div className="qa-item" key={href}>
            <a href={href} className="strat" style={{ color: "var(--primary-deep)", fontWeight: 800 }}>{label}</a>
          </div>
        ))}
      </div>
    </section>
  );
}

function FAQ({ faqs }) {
  const [open, setOpen] = useState(0);
  return (
    <section className="section" id="faq">
      <div className="section-head">
        <span className="eyebrow"><span className="dot" /> FAQ</span>
        <h2 className="h2">Frequently asked questions.</h2>
      </div>
      <div className="faq-stack">
        {faqs.map(([question, answer], index) => (
          <div className={`faq-item ${open === index ? "open" : ""}`} key={question}>
            <button className="faq-q" onClick={() => setOpen(open === index ? -1 : index)}><span>{question}</span><span className="plus">+</span></button>
            <div className="faq-a">{answer}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function Related({ page }) {
  return (
    <section className="section" id="related">
      <div className="tool-bridge">
        <div>
          <h3>{page.relatedTitle || "Keep the whole listing consistent."}</h3>
          <p>{page.relatedIntro || "Use this page as the rule check, then move into ShopFoundry when you need title, tags, and description to work together."}</p>
          <ul>
            {page.related.map(([href, label]) => <li key={href}><a href={href} style={{ color: "var(--primary-deep)", fontWeight: 800 }}>{label}</a></li>)}
          </ul>
        </div>
        <a className="btn btn-primary" href={page.secondaryCta.href}>{page.secondaryCta.label}</a>
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer>
      <div className="wrap" style={{ display: "grid", gridTemplateColumns: "minmax(220px,1.4fr) repeat(3, 1fr)", gap: 40, padding: "52px 0 28px" }}>
        <div>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
            <img src="/logo.png" alt="ShopFoundry" style={{ width: 28, height: 28, borderRadius: 6 }} />
            <span style={{ fontFamily: "'Fraunces',serif", fontWeight: 700, fontSize: 17, color: "#2C2C24" }}>ShopFoundry</span>
          </div>
          <p style={{ color: "#78786C", fontSize: 14, lineHeight: 1.6, maxWidth: 260, margin: 0 }}>Tool-first Etsy SEO pages that feed one focused listing workspace.</p>
        </div>
        <div>
          <div style={{ fontWeight: 700, fontSize: 14, color: "#2C2C24", marginBottom: 14 }}>Tools</div>
          <a className="foot-link" href="/etsy-title-generator">Etsy Title Generator</a>
          <a className="foot-link" href="/etsy-tag-generator">Etsy Tag Generator</a>
          <a className="foot-link" href="/etsy-description-generator">Etsy Description Generator</a>
        </div>
        <div>
          <div style={{ fontWeight: 700, fontSize: 14, color: "#2C2C24", marginBottom: 14 }}>Character limits</div>
          <a className="foot-link" href="/etsy-title-character-limit">Title Character Limit</a>
          <a className="foot-link" href="/etsy-tag-character-limit">Tag Character Limit</a>
          <a className="foot-link" href="/etsy-description-character-limit">Description Character Limit</a>
          <a className="foot-link" href="/etsy-t-shirt-title-character-limit">T-Shirt Title Limit</a>
        </div>
        <div>
          <div style={{ fontWeight: 700, fontSize: 14, color: "#2C2C24", marginBottom: 14 }}>Product</div>
          <a className="foot-link" href="/etsy-listing-generator">Listing Workspace</a>
          <a className="foot-link" href="/pricing">Pricing</a>
        </div>
      </div>
      <div className="wrap" style={{ borderTop: "1px solid rgba(222,216,207,0.6)", padding: "20px 0", color: "#9a9a8c", fontSize: 13 }}>© 2026 · ShopFoundry. Not affiliated with Etsy, Inc.</div>
    </footer>
  );
}

function App() {
  const page = getPage();
  return (
    <>
      <Nav />
      <Hero page={page} />
      <ContentSections page={page} />
      <Footer />
    </>
  );
}

window.CHARACTER_LIMIT_CLUSTER_VERSION = CLUSTER_VERSION;
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
