> ## Documentation Index
> Fetch the complete documentation index at: https://na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Creative Projects Built with Livepeer

> Be inspired by this collection of creative ecosystem use cases re-inventing their industries with Livepeer.

export const showcaseData = [{
  "title": "nytv.live",
  "subtitle": "New York City's free, independent 24/7 TV network",
  "href": "https://nytv.live",
  "mediaSrc": "https://raw.githubusercontent.com/livepeer/docs/docs-v2-preview/snippets/assets/domain/00_HOME/showcase/nytv.live/mediaSrc",
  "mediaType": "video",
  "logo": "https://raw.githubusercontent.com/livepeer/docs/docs-v2-preview/snippets/assets/domain/00_HOME/showcase/nytv.live/logo",
  "categoryTags": ["Video Streaming", "Events & Communications"],
  "productTags": ["Livepeer Network"],
  "description": "NYTV is a free, independent internet TV network that broadcasts live shows, documentaries, and news programs 24/7 from New York City.  It uses Livepeer for its video streaming infrastructure, ensuring low latency and high quality for its viewers, while keeping costs low",
  "cta": "Watch now! : https://nytv.live/",
  "links": [{
    "instagram": "https://instagram.com/newyorktelevision"
  }, {
    "bluesky": "https://bsky.app/profile/nytv.live"
  }, {
    "website": "https://nytv.live"
  }, {
    "email": "info@nytv.live"
  }]
}, {
  "title": "The Lot Radio",
  "subtitle": "24/7 livestream of 165+ resident DJs from Brooklyn",
  "href": "https://www.thelotradio.com",
  "mediaSrc": "/snippets/assets/media/videos/the-lott-hero-video.mp4",
  "mediaType": "video",
  "logo": "",
  "categoryTags": ["Video Streaming", "Community"],
  "productTags": ["Livepeer Network"],
  "description": "The Lot Radio is a 24/7 independent internet radio station and live video stream broadcasting from a converted shipping container in Brooklyn, New York. Over 165 resident DJs perform live sets streamed via Livepeer infrastructure. A beloved cultural institution blending music, art, and community -powered by decentralised video infrastructure for reliable, low-cost global delivery.",
  "cta": "Tune in : https://www.thelotradio.com/",
  "links": [{
    "instagram": "https://instagram.com/thelotradio"
  }, {
    "website": "https://www.thelotradio.com"
  }, {
    "youtube": "https://youtube.com/thelotradio"
  }]
}, {
  "title": "Huddle01",
  "subtitle": "The first decentralised real-time communication network",
  "href": "https://huddle01.com",
  "mediaSrc": "https://framerusercontent.com/assets/T132RTPxL9aeFbpdvkm9HyLS9pQ.mp4",
  "mediaType": "video",
  "logo": "",
  "categoryTags": ["Video Streaming", "Apps"],
  "productTags": ["Livepeer Network"],
  "description": "Huddle01 is the first web3-native video conferencing solution built with decentralisation, security, and performance at its core. Uses Livepeer for video transcoding and IPFS for distributed storage. Free to use, no registration required, and no user tracking. Features include token-gated meetings, NFT rewards, and crypto tipping -a complete dRTC (decentralised real-time communication) network.",
  "cta": "Start a meeting : https://huddle01.com/",
  "links": [{
    "github": "https://github.com/Huddle01"
  }, {
    "website": "https://huddle01.com"
  }, {
    "x": "https://x.com/huddle01com"
  }, {
    "discord": "https://discord.com/invite/huddle01"
  }]
}, {
  "title": "Fishtank Live",
  "subtitle": "24/7 interactive reality livestream -1M+ viewers",
  "href": "https://www.fishtank.live",
  "mediaSrc": "https://img.youtube.com/vi/aP1CK26t3Ws/maxresdefault.jpg",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Video Streaming", "Community"],
  "productTags": ["Livepeer Studio"],
  "description": "Fishtank Live is an interactive 24/7 reality livestream where contestants are monitored around the clock. Viewers influence the show in real time by purchasing interactive features -sound effects, text-to-speech, and direct contestant interactions. Powered by Livepeer Studio, Fishtank reached 1 million unique viewers and $3 million in revenue, saving over 55% on streaming costs.",
  "cta": "Watch Fishtank : https://www.fishtank.live/",
  "links": [{
    "website": "https://www.fishtank.live"
  }, {
    "x": "https://x.com/fiaborern"
  }]
}, {
  "title": "Picarto.tv",
  "subtitle": "The world's best creative live streaming service",
  "href": "https://picarto.tv",
  "mediaSrc": "https://media1.giphy.com/media/2A8saXanZmdXPmHVtB/giphy.gif",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Video Streaming", "Community"],
  "productTags": ["Livepeer Network"],
  "description": "Picarto.tv is the leading live streaming platform for digital artists and creatives. Watch your favourite artists draw, paint, sculpt, and create in real time. With dedicated categories for art, gaming, and creative content, Picarto serves a global community of creators who livestream their creative process. Powered by Livepeer for reliable, low-latency video delivery at scale.",
  "cta": "Watch artists live : https://picarto.tv/",
  "links": [{
    "website": "https://picarto.tv"
  }, {
    "x": "https://x.com/PicartoTV"
  }, {
    "discord": "https://discord.gg/picarto"
  }]
}, {
  "title": "StreamETH",
  "subtitle": "Open-source livestreaming for Ethereum events",
  "href": "https://streameth.org",
  "mediaSrc": "https://img.youtube.com/vi/Up3pKJKvsAE/maxresdefault.jpg",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Video Streaming", "Community"],
  "productTags": ["Livepeer Network"],
  "description": "StreamETH is the open-source live-streaming and video-on-demand platform built for Ethereum events. Born from a collaboration with the Ethereum Foundation for Devconnect, it provides fullstack decentralised video solutions for web3 event organisers. True to web3 values: open source, user-owned, and adaptable. Powers events like ETHGlobal, Devcon, and major Ethereum conferences worldwide.",
  "cta": "Explore StreamETH : https://streameth.org/",
  "links": [{
    "github": "https://github.com/streamethorg"
  }, {
    "website": "https://streameth.org"
  }, {
    "x": "https://x.com/streamaboreth"
  }]
}, {
  "title": "Tape",
  "subtitle": "Video-centric social media built on Lens Protocol",
  "href": "https://tape.xyz",
  "mediaSrc": "https://raw.githubusercontent.com/tapexyz/assets/main/hello.gif",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Social Media", "Video Streaming"],
  "productTags": ["Livepeer Network"],
  "description": "Tape (formerly Lenstube) is a decentralised video-sharing platform built on Lens Protocol and Livepeer. Think YouTube for web3 -with an explore page, a short-form 'Bytes' section (like TikTok), creator tipping with MATIC, and the ability to curate videos as NFTs. Livepeer handles all video processing and playback while Lens provides the social graph. Fully community-owned.",
  "cta": "Watch on Tape : https://tape.xyz/",
  "links": [{
    "github": "https://github.com/tapexyz/tape"
  }, {
    "website": "https://tape.xyz"
  }, {
    "x": "https://x.com/tapexyz"
  }]
}, {
  "title": "Hey",
  "subtitle": "Community-built decentralised social media on Lens",
  "href": "https://hey.xyz",
  "mediaSrc": "https://img.youtube.com/vi/IhT2JSsmf1Y/maxresdefault.jpg",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Social Media"],
  "productTags": ["Livepeer Network"],
  "description": "Hey (formerly Lenster) is the most popular social media app built on Lens Protocol. A permissionless, community-built platform for publishing, sharing, and engaging with content -with video powered by Livepeer. Supports NFT-gated communities, decentralised identity, and composable social interactions. No algorithms deciding what you see -your feed, your rules.",
  "cta": "Join Hey : https://hey.xyz/",
  "links": [{
    "github": "https://github.com/heyxyz/hey"
  }, {
    "website": "https://hey.xyz"
  }, {
    "x": "https://x.com/heaboredotxyz"
  }]
}, {
  "title": "Bonfire",
  "subtitle": "No-code community platform for web3 creators",
  "href": "https://www.bonfire.xyz",
  "mediaSrc": "https://img.youtube.com/vi/k1Dtms2qVbs/maxresdefault.jpg",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Apps", "Community"],
  "productTags": ["Livepeer Studio"],
  "description": "Bonfire enables creators, communities, and brands to build custom web3 platforms powered by social tokens and NFTs. A no-code website builder with token-gated content, merchandise, ticket sales, video on demand, and livestreaming -all using Livepeer Studio. Disrupting the web2 creator economy by giving creators ownership of their platforms and direct monetisation of their audiences.",
  "cta": "Build with Bonfire : https://www.bonfire.xyz/",
  "links": [{
    "website": "https://www.bonfire.xyz"
  }, {
    "x": "https://x.com/BonfireXYZ"
  }]
}, {
  "title": "Minds",
  "subtitle": "Open-source social network with 15M+ users",
  "href": "https://www.minds.com",
  "mediaSrc": "https://cms.minds.com/uploads/networks_app_overview_0f2510abe5.mp4",
  "mediaType": "video",
  "logo": "",
  "categoryTags": ["Social Media", "Apps"],
  "productTags": ["Livepeer Studio"],
  "description": "Minds is an open-source social network and community platform with over 15 million users. Prioritises free speech, user privacy, and creator monetisation. Live streaming -its top-requested feature -is powered by Livepeer Studio, providing reliable, low-cost video infrastructure at scale. Available as web, iOS, and Android apps with full white-label capabilities for brands and organisations.",
  "cta": "Join Minds : https://www.minds.com/",
  "links": [{
    "github": "https://github.com/minds"
  }, {
    "website": "https://www.minds.com"
  }, {
    "x": "https://x.com/minds"
  }]
}, {
  "title": "Unlonely",
  "subtitle": "Crypto-native livestreaming with gamified interaction",
  "href": "https://www.unlonely.app",
  "mediaSrc": "https://img.youtube.com/vi/DoLz53_3-WE/maxresdefault.jpg",
  "mediaType": "image",
  "logo": "",
  "categoryTags": ["Video Streaming", "Community"],
  "productTags": ["Livepeer Network"],
  "description": "Unlonely is a crypto-native livestreaming platform where creators launch streamer-specific tokens and host live betting markets. Known for 'Love on Leverage' -live-streamed first dates where viewers bet ETH on whether there will be a second date. Combines entertainment with on-chain interaction, using Livepeer for reliable decentralised streaming infrastructure.",
  "cta": "Watch live : https://www.unlonely.app/",
  "links": [{
    "website": "https://www.unlonely.app"
  }, {
    "x": "https://x.com/unlaboreonely"
  }]
}];

export const ShowcaseCards = ({items = [], limit = null, pageSize = 10, className = "", style = {}, ...rest}) => {
  const [search, setSearch] = useState("");
  const [categoryFilter, setCategoryFilter] = useState("All");
  const [productFilter, setProductFilter] = useState("All");
  const [page, setPage] = useState(1);
  const allCategories = ["All", ...Array.from(new Set(items.flatMap(item => item.categoryTags || []))).sort()];
  const allProducts = ["All", ...Array.from(new Set(items.flatMap(item => (item.productTags || []).map(t => t.replace(/\s*\(.*?\)\s*/g, ""))))).sort()];
  const filtered = items.filter(item => {
    const matchesSearch = !search || item.title?.toLowerCase().includes(search.toLowerCase()) || item.description?.toLowerCase().includes(search.toLowerCase()) || item.subtitle?.toLowerCase().includes(search.toLowerCase());
    const matchesCategory = categoryFilter === "All" || (item.categoryTags || []).includes(categoryFilter);
    const matchesProduct = productFilter === "All" || (item.productTags || []).map(t => t.replace(/\s*\(.*?\)\s*/g, "")).includes(productFilter);
    return matchesSearch && matchesCategory && matchesProduct;
  });
  const totalItems = limit ? Math.min(filtered.length, limit) : filtered.length;
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
  const safePage = Math.min(page, totalPages);
  const paginatedItems = filtered.slice(0, limit || filtered.length).slice((safePage - 1) * pageSize, safePage * pageSize);
  useEffect(() => {
    setPage(1);
  }, [search, categoryFilter, productFilter]);
  const ShowcaseCard = ({mediaSrc: _mediaSrc, mediaType = "image", logo = "", title = "", subtitle = "", description = "", href = "#", categoryTags = ["Other"], productTags = ["Livepeer"], cta = "", links = [], contact = [], style = {}, arrow = false, ...cardProps}) => {
    const defaultMedia = "/snippets/assets/domain/00_HOME/Hero_Images/hero_showcase.png";
    const mediaSrc = _mediaSrc || defaultMedia;
    const isVideo = mediaType === "video";
    const isImage = mediaType === "image";
    const categoryBadgeKeys = {
      Apps: "blue",
      "Social Media": "purple",
      "Video Streaming": "green",
      Community: "yellow",
      Marketplaces: "orange",
      "Developer Tools": "red",
      "AI-Powered Apps": "cyan",
      default: "gray"
    };
    const productTagHrefKeys = {
      Daydream: ["../../../10_products/daydream/overview.mdx", "https://daydream.live/"],
      "Stream.place": ["../../../10_products/streamplace/overview.mdx", "https://stream.place/"],
      "Livepeer Studio": ["../../../10_products/livepeer-studio/livepeer-studio.mdx", "https://livepeer.studio/"],
      Frameworks: ["../../../10_products/frameworks/overview.mdx", "https://frameworks.network/"],
      "Livepeer Network": ["../../../01_about/concepts/overview.mdx", "https://livepeer.org/"],
      default: ["../../../10_products/products/ecosystem-products.mdx", ""]
    };
    const linkIcons = {
      github: "github",
      youtube: "youtube",
      blog: "newspaper",
      x: "x-twitter",
      instagram: "instagram",
      email: "envelope",
      website: "globe-pointer",
      forum: "message",
      discord: "discord",
      bluesky: "bluesky",
      telegram: "telegram",
      linkedin: "linkedin",
      whatsapp: "whatsapp",
      snapchat: "snapchat",
      reddit: "reddit",
      twitch: "twitch",
      android: "google-play",
      other: "link"
    };
    const mediaContainerStyle = {
      position: "relative",
      display: "block",
      overflow: "hidden",
      isolation: "isolate",
      marginLeft: "-1.5rem",
      marginRight: "-2.5rem",
      marginTop: "-1.5rem",
      borderRadius: 0,
      maxHeight: "300px",
      width: "calc(100% + 4rem)",
      ...style
    };
    const mediaStyle = {
      display: "block",
      width: "100%",
      height: "300px",
      margin: "0",
      padding: "0",
      borderRadius: 0,
      objectFit: "cover"
    };
    const titleContainerStyle = {
      display: "flex",
      justifyContent: "center",
      zIndex: "10",
      pointerEvents: "none"
    };
    const titleStyle = {
      position: "absolute",
      top: "0",
      marginTop: "var(--lp-spacing-2)",
      background: "var(--lp-color-bg-overlay)",
      color: "var(--lp-color-on-accent)",
      fontSize: "18px",
      fontWeight: "800",
      padding: "6px 16px",
      borderRadius: "6px",
      border: "1px solid var(--lp-color-border-inverse)",
      ...style
    };
    const logoStyle = {
      position: "absolute",
      bottom: "0",
      width: "60%",
      height: "auto",
      objectFit: "contain",
      alignSelf: "flex-end",
      opacity: 0.8,
      borderRadius: "6px",
      margin: 0,
      padding: 0,
      marginBottom: "var(--lp-spacing-2)"
    };
    const logoMediaStyle = {
      position: "absolute",
      top: "50%",
      transform: "translateY(-50%)",
      width: "101%",
      height: "auto",
      objectFit: "contain",
      alignSelf: "center",
      justifySelf: "center",
      opacity: 1,
      borderRadius: "6px",
      margin: 0,
      padding: 0,
      marginBottom: "var(--lp-spacing-2)"
    };
    const subtitleContainerStyle = {
      display: "flex",
      flexDirection: "column",
      gap: "var(--lp-spacing-4)",
      margin: "1rem 0",
      width: "calc(100% + 1.5rem)",
      marginRight: "-1.5rem"
    };
    const subtitleArrowStyle = {
      display: "flex",
      width: "100%",
      justifyContent: "space-between",
      alignItems: "center",
      flexDirection: "row"
    };
    const subtitleStyle = {
      color: "var(--lp-color-text-primary)",
      width: "100%",
      fontStyle: "normal",
      fontWeight: "bold",
      fontSize: "1rem"
    };
    const scrollBoxStyle = {
      color: "var(--lp-color-text-secondary)",
      fontStyle: "italic"
    };
    const linkIconContainerStyle = {
      display: "flex",
      justifyContent: "center",
      gap: "var(--lp-spacing-2)",
      marginTop: "var(--lp-spacing-4)",
      paddingTop: "var(--lp-spacing-2)",
      width: "fit-content",
      justifySelf: "center"
    };
    const linkIconStyle = {
      borderBottom: "none",
      fontWeight: "bold",
      fontSize: "11px",
      marginLeft: "var(--lp-spacing-1)"
    };
    const dividerStyle = {
      margin: 0,
      marginBottom: "-1rem",
      padding: 0,
      fontSize: "10px",
      minWidth: "70%",
      maxWidth: "85%",
      alignSelf: "center",
      justifySelf: "center",
      width: "90%"
    };
    const productTagContainerStyle = {
      display: "flex",
      justifyContent: "center",
      alignItems: "center",
      alignSelf: "center",
      justifySelf: "center",
      fontSize: "10px",
      margin: 0,
      padding: 0
    };
    const productTagStyle = {
      borderBottom: "none",
      color: "var(--lp-color-accent)",
      fontWeight: "bold",
      fontSize: "11px",
      marginLeft: "var(--lp-spacing-1)"
    };
    const isDefaultMedia = mediaSrc === defaultMedia;
    const logoMediaCard = <div style={{
      ...mediaContainerStyle,
      height: "300px"
    }}>
        <div style={titleContainerStyle}>
          {logo && <img src={logo} alt={title} style={logoMediaStyle} />}
          <span style={titleStyle}>{title}</span>
        </div>
      </div>;
    const mediaCard = <div style={mediaContainerStyle}>
        {}
        {isVideo && <video autoPlay muted loop playsInline src={mediaSrc} style={mediaStyle} />}
        {isImage && <img src={mediaSrc} alt={title} style={mediaStyle} />}
        {}
        {}
        <div style={titleContainerStyle}>
          {logo && <img src={logo} alt={title} style={logoStyle} />}
          <span style={titleStyle}>{title}</span>
        </div>
        {}
      </div>;
    const renderSubtitleArrow = <div style={subtitleArrowStyle}>
        <Subtitle text={subtitle} style={subtitleStyle} />
        <Icon icon="arrow-up-right" iconType="solid" size={14} style={{
      flexShrink: 0
    }} color="var(--lp-color-text-muted)" />
      </div>;
    const renderCategoryTags = <div style={{
      display: "flex",
      flexWrap: "nowrap",
      gap: "var(--lp-spacing-1)"
    }}>
        {categoryTags.map(tag => <Badge key={tag} color={categoryBadgeKeys[tag] || categoryBadgeKeys["default"]}>
            {tag}
          </Badge>)}
      </div>;
    const renderLinkIcons = <div style={linkIconContainerStyle}>
        {links.map((link, index) => {
      if (!link || typeof link !== "object") {
        return null;
      }
      const [key, value] = Object.entries(link)[0] || [];
      if (!key || !value) {
        return null;
      }
      return <a href={value} target="_blank" rel="noopener noreferrer" key={`${key}-${index}`} style={linkIconStyle} aria-label={`Open ${key} link`} rel="noreferrer noopener">
              <Tooltip tip={value}>
                <Icon icon={linkIcons[key] || "link"} iconType="solid" size={16} color="var(--lp-color-text-secondary)" />
              </Tooltip>
            </a>;
    })}
      </div>;
    const renderProductTags = <div style={productTagContainerStyle}>
        Powered by
        {productTags.slice(0, 1).map(tag => <a href={(productTagHrefKeys[tag] || productTagHrefKeys["default"])[1]} target="_blank" rel="noopener noreferrer" key={tag} style={productTagStyle}>
            {tag}{" "}
            <Icon icon="arrow-up-right" iconType="solid" size={10} color="var(--lp-color-text-secondary)" />
          </a>)}
      </div>;
    return <Card href={href} arrow={false} {...cardProps}>
        {isDefaultMedia && logo ? logoMediaCard : mediaCard}
        <div style={subtitleContainerStyle}>
          {renderSubtitleArrow}
          {renderCategoryTags}
        </div>
        <div style={{
      ...scrollBoxStyle,
      height: "150px",
      overflowY: "auto"
    }} role="region" tabIndex={0} aria-label={title ? `Scrollable description for ${title}` : "Scrollable description"}>
          {description}
          {renderLinkIcons}
        </div>
        <div>
          <CustomDivider style={dividerStyle} />
          {renderProductTags}
        </div>
      </Card>;
  };
  const filterBarStyle = {
    display: "flex",
    flexWrap: "wrap",
    gap: "var(--lp-spacing-2)",
    alignItems: "center",
    marginBottom: "var(--lp-spacing-4)"
  };
  const inputStyle = {
    flex: "1 1 200px",
    padding: "0.5rem 0.75rem",
    borderRadius: "6px",
    border: "1px solid var(--lp-color-border-default)",
    background: "var(--lp-color-bg-page)",
    color: "var(--lp-color-text-secondary)",
    fontSize: "0.875rem",
    outline: "revert"
  };
  const selectStyle = {
    padding: "0.5rem 0.75rem",
    borderRadius: "6px",
    border: "1px solid var(--lp-color-border-default)",
    background: "var(--lp-color-bg-page)",
    color: "var(--lp-color-text-secondary)",
    fontSize: "0.875rem",
    outline: "revert",
    cursor: "pointer"
  };
  const paginationStyle = {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    gap: "var(--lp-spacing-3)",
    marginTop: "var(--lp-spacing-6)"
  };
  const pageButtonStyle = disabled => ({
    padding: "0.4rem 1rem",
    borderRadius: "6px",
    border: "1px solid var(--lp-color-border-default)",
    background: disabled ? "transparent" : "var(--lp-color-bg-page)",
    color: disabled ? "var(--lp-color-border-default)" : "var(--lp-color-text-secondary)",
    cursor: disabled ? "default" : "pointer",
    fontSize: "0.875rem",
    opacity: disabled ? 0.5 : 1
  });
  return <div className={className} style={style} {...rest}>
      <div style={filterBarStyle}>
        <input type="text" placeholder="Search projects..." value={search} onChange={e => setSearch(e.target.value)} style={inputStyle} aria-label="Search projects" />
        <select value={categoryFilter} onChange={e => setCategoryFilter(e.target.value)} style={selectStyle} aria-label="Filter by category">
          {allCategories.map(cat => <option key={cat} value={cat}>
              {cat === "All" ? "All Categories" : cat}
            </option>)}
        </select>
        <select value={productFilter} onChange={e => setProductFilter(e.target.value)} style={selectStyle} aria-label="Filter by product">
          {allProducts.map(prod => <option key={prod} value={prod}>
              {prod === "All" ? "All Products" : prod}
            </option>)}
        </select>
      </div>
      {paginatedItems.length === 0 ? <div style={{
    textAlign: "center",
    padding: "var(--lp-spacing-8)",
    color: "var(--lp-color-text-secondary)",
    opacity: 0.6
  }}>
          No projects found.
        </div> : <Columns cols={2}>
          {paginatedItems.map(item => <ShowcaseCard key={item.title} {...item} />)}
        </Columns>}
      {totalPages > 1 && <div style={paginationStyle}>
          <button style={pageButtonStyle(safePage <= 1)} onClick={() => setPage(p => Math.max(1, p - 1))} disabled={safePage <= 1}>
            ← Prev
          </button>
          <span style={{
    fontSize: "0.875rem",
    color: "var(--lp-color-text-secondary)"
  }}>
            Page {safePage} of {totalPages}
          </span>
          <button style={pageButtonStyle(safePage >= totalPages)} onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={safePage >= totalPages}>
            Next →
          </button>
        </div>}
    </div>;
};

export const ShowcaseVideo = ({src, title, subtitle, className = "", style = {}, ...rest}) => {
  return <TitledVideo className={className} {...rest} src={src} title={title} subtitle={subtitle} style={{
    marginLeft: "-1.5rem",
    marginRight: "-2.5rem",
    marginTop: "-1.5rem",
    borderRadius: 0,
    maxHeight: "300px",
    width: "calc(100% + 4rem)",
    ...style
  }} />;
};

export const TitledVideo = ({src, title, subtitle, arrow = false, borderRadius = "var(--lp-spacing-px-12)", style = {}, className = "", ...rest}) => {
  const prefersReducedMotion = typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  return <div className={className} style={{
    position: "relative",
    display: "block",
    borderRadius,
    overflow: "hidden",
    isolation: "isolate",
    ...style
  }} {...rest}>
      <video autoPlay={!prefersReducedMotion} muted loop={!prefersReducedMotion} playsInline src={src} style={{
    display: "block",
    width: "100%",
    height: "auto",
    margin: "0",
    padding: "0"
  }} />
      {title && <div style={{
    position: "absolute",
    top: "0",
    left: "0",
    right: "0",
    bottom: "0",
    display: "flex",
    paddingTop: "var(--lp-spacing-2)",
    alignItems: "flex-start",
    justifyContent: "center",
    zIndex: "10",
    pointerEvents: "none"
  }}>
            <span style={{
    background: "var(--lp-color-bg-overlay)",
    color: "var(--lp-color-on-accent)",
    fontSize: "18px",
    fontWeight: "800",
    padding: "6px 16px",
    borderRadius: "6px",
    border: "1px solid var(--lp-color-border-inverse)"
  }}>
            {title}
            <br />
            {subtitle}
          </span>
        </div>}
      {arrow && <div style={{
    position: "absolute",
    top: "0",
    left: "0",
    right: "0",
    bottom: "0",
    display: "flex",
    paddingTop: "var(--lp-spacing-2)",
    paddingRight: "var(--lp-spacing-2)",
    alignItems: "flex-start",
    justifyContent: "right",
    zIndex: "10",
    pointerEvents: "none"
  }}>
          <span>
            <Icon icon="arrow-up-right" size={14} color="var(--lp-color-on-accent)" />
          </span>
        </div>}
    </div>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

export const Subtitle = ({style = {}, text, children, variant = 'default', className = '', ...rest}) => {
  const renderInlineCode = (value, keyPrefix) => {
    return value.split(/(`[^`]+`)/g).map((segment, index) => {
      if (segment.startsWith('`') && segment.endsWith('`')) {
        return <code key={`${keyPrefix}-code-${index}`}>{segment.slice(1, -1)}</code>;
      }
      return segment;
    });
  };
  const renderInlineMarkup = (value, keyPrefix = 'subtitle') => {
    if (typeof value !== 'string') {
      return value;
    }
    return value.split(/(\*\*[\s\S]+?\*\*)/g).map((segment, index) => {
      if (segment.startsWith('**') && segment.endsWith('**')) {
        const inner = segment.slice(2, -2);
        return <strong key={`${keyPrefix}-strong-${index}`}>
            {renderInlineCode(inner, `${keyPrefix}-strong-${index}`)}
          </strong>;
      }
      return renderInlineCode(segment, `${keyPrefix}-${index}`);
    });
  };
  const renderContent = (value, keyPrefix) => {
    if (Array.isArray(value)) {
      return value.map((item, index) => renderContent(item, `${keyPrefix}-${index}`));
    }
    return renderInlineMarkup(value, keyPrefix);
  };
  const variants = {
    default: {
      fontSize: '1rem',
      fontStyle: 'italic',
      color: 'var(--lp-color-accent)',
      marginBottom: 0
    },
    changelog: {
      fontSize: '0.8rem',
      fontStyle: 'normal',
      fontWeight: 700,
      color: 'var(--lp-color-text-primary)',
      marginBottom: 0
    }
  };
  const base = variants[variant] || variants.default;
  return <span className={className} style={{
    ...base,
    ...style
  }} {...rest}>
      {renderContent(text, 'text')}
      {renderContent(children, 'children')}
    </span>;
};

export const ScrollBox = ({children, maxHeight = 300, showHint = true, ariaLabel = "Scrollable content", style = {}, className = "", ...rest}) => {
  const contentRef = useRef(null);
  const [isOverflowing, setIsOverflowing] = useState(false);
  useEffect(() => {
    const checkOverflow = () => {
      if (contentRef.current) {
        const maxHeightPx = typeof maxHeight === "number" ? maxHeight : parseInt(maxHeight, 10) || 300;
        setIsOverflowing(contentRef.current.scrollHeight > maxHeightPx);
      }
    };
    checkOverflow();
    window.addEventListener("resize", checkOverflow);
    return () => window.removeEventListener("resize", checkOverflow);
  }, [maxHeight, children]);
  return <div className={className} style={{
    position: "relative",
    ...style
  }} {...rest}>
      <div ref={contentRef} role="region" tabIndex={0} aria-label={ariaLabel} style={{
    maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight,
    overflowY: "auto",
    paddingRight: 4
  }} onScroll={e => {
    const el = e.target;
    const atBottom = el.scrollHeight - el.scrollTop <= el.clientHeight + 10;
    const hint = el.parentNode.querySelector("[data-scroll-hint]");
    if (hint) hint.style.opacity = atBottom ? "0" : "1";
  }}>
        {children}
      </div>
      {showHint && isOverflowing && <div data-scroll-hint style={{
    fontSize: 11,
    color: "var(--lp-color-text-muted)",
    textAlign: "center",
    marginTop: 8,
    transition: "opacity 0.2s"
  }}>
          Scroll for more ↓
        </div>}
    </div>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

*Explore and be inspired by these Livepeer-powered projects – from AI video playgrounds to 24/7 reality shows, decentralised social networks to event streaming platforms.*

<Tip> <h3>Want to be featured here? </h3><LinkArrow label="Share your project!" href="https://docs.google.com/forms/d/e/1FAIpQLSeJ4uoxQ9LUSg89RDWcGiTsbePXsSkRXzGzrdyJ8RVrM1171Q/viewform?usp=sharing&ouid=103197690869380433914" newline={false} /> </Tip>

<ShowcaseCards items={showcaseData} />
