> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Gateway Home Portal

> Welcome To The Gateway Portal: Explore, Navigate, Create

export const CustomCardTitle = ({icon, title, variant = "card", iconSize, style = {}, className = "", ...rest}) => {
  const variants = {
    card: {
      display: 'flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)",
      marginBottom: "var(--lp-spacing-3)",
      color: 'var(--lp-color-text-primary)',
      fontSize: '1rem',
      fontWeight: 600
    },
    accordion: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)"
    },
    tab: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.4rem',
      fontSize: '0.875rem'
    }
  };
  const sizes = {
    card: 20,
    accordion: 18,
    tab: 14
  };
  const size = iconSize || sizes[variant] || 20;
  const baseStyle = variants[variant] || variants.card;
  return variant === 'card' ? <div className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </div> : <span className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </span>;
};

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>;
};

export const Starfield = ({density = 1.1, className = "", style = {}, ...rest}) => {
  const canvasRef = useRef(null);
  const readThemeColor = (tokenName, fallback) => {
    if (typeof window === "undefined") return fallback;
    const value = getComputedStyle(document.documentElement).getPropertyValue(tokenName).trim();
    return value || fallback;
  };
  const brightenHex = (hexColor, amount) => {
    const normalized = String(hexColor || "").replace("#", "");
    const expanded = normalized.length === 3 ? normalized.split("").map(char => `${char}${char}`).join("") : normalized;
    if (!(/^[0-9a-fA-F]{6}$/).test(expanded)) {
      return hexColor;
    }
    const channels = expanded.match(/.{2}/g).map(chunk => Number.parseInt(chunk, 16));
    const adjusted = channels.map(channel => Math.max(0, Math.min(255, Math.round(channel + (255 - channel) * amount))));
    return `#${adjusted.map(channel => channel.toString(16).padStart(2, "0")).join("")}`;
  };
  const isDarkMode = () => {
    if (typeof window === "undefined") return false;
    return document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
  };
  const getColorPalette = () => {
    const accent = readThemeColor("--accent", "#5DD662");
    const accentDark = readThemeColor("--accent-dark", accent);
    return isDarkMode() ? [accent, accent, accentDark, brightenHex(accent, 0.18)] : [brightenHex(accent, 0.22), brightenHex(accent, 0.22), brightenHex(accent, 0.38), brightenHex(accent, 0.52)];
  };
  const SIZE_BUCKETS = [{
    scale: 0.3,
    weight: 0.50
  }, {
    scale: 0.5,
    weight: 0.20
  }, {
    scale: 0.9,
    weight: 0.15
  }, {
    scale: 1.3,
    weight: 0.10
  }, {
    scale: 1.8,
    weight: 0.05
  }];
  const pickScale = () => {
    const r = Math.random();
    let acc = 0;
    for (const b of SIZE_BUCKETS) {
      acc += b.weight;
      if (r <= acc) return b.scale;
    }
    return 0.25;
  };
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || typeof window === "undefined" || typeof document === "undefined" || typeof Image === "undefined") {
      return;
    }
    const ctx = canvas.getContext("2d");
    if (!ctx) {
      console.warn("[Starfield] Canvas 2D context unavailable");
      return;
    }
    let rafId;
    let stars = [];
    let tintedCache = new Map();
    let cancelled = false;
    const COLORS = Array.isArray(getColorPalette()) ? getColorPalette().filter(Boolean) : [];
    if (COLORS.length === 0) {
      console.warn("[Starfield] Missing color palette");
      return;
    }
    const logo = new Image();
    logo.src = "/snippets/assets/logos/Livepeer-Logo-Symbol-Light.svg";
    const makeTinted = color => {
      if (tintedCache.has(color)) return tintedCache.get(color);
      const off = document.createElement("canvas");
      const octx = off.getContext("2d");
      if (!octx) {
        return null;
      }
      const base = 32;
      off.width = base;
      off.height = base;
      octx.clearRect(0, 0, base, base);
      octx.drawImage(logo, 0, 0, base, base);
      octx.globalCompositeOperation = "source-in";
      octx.fillStyle = color;
      octx.fillRect(0, 0, base, base);
      octx.globalCompositeOperation = "source-over";
      tintedCache.set(color, off);
      return off;
    };
    const resize = () => {
      const dpr = window.devicePixelRatio || 1;
      const rect = canvas.getBoundingClientRect();
      if (!rect.width || !rect.height) {
        return;
      }
      canvas.width = rect.width * dpr;
      canvas.height = rect.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      const count = Math.floor(rect.width * rect.height / 16000 * density);
      stars = Array.from({
        length: count
      }).map(() => {
        const color = COLORS[Math.floor(Math.random() * COLORS.length)];
        return {
          x: Math.random() * rect.width,
          y: Math.random() * rect.height,
          scale: pickScale(),
          base: Math.random() * 0.45 + 0.25,
          speed: Math.random() * 0.015 + 0.003,
          phase: Math.random() * Math.PI * 2,
          color,
          rotation: Math.random() * Math.PI * 2,
          rotSpeed: Math.random() * 0.002 - 0.001
        };
      });
    };
    const draw = () => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      for (const s of stars) {
        s.phase += s.speed;
        s.rotation += s.rotSpeed;
        const alpha = s.base + Math.sin(s.phase) * 0.25;
        const size = 10 * s.scale;
        ctx.globalAlpha = Math.max(0, Math.min(1, alpha));
        const tinted = makeTinted(s.color);
        if (!tinted) {
          continue;
        }
        ctx.save();
        ctx.translate(s.x, s.y);
        ctx.rotate(s.rotation);
        ctx.drawImage(tinted, -size / 2, -size / 2, size, size);
        ctx.restore();
      }
      rafId = requestAnimationFrame(draw);
    };
    logo.onload = () => {
      if (cancelled) {
        return;
      }
      const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      resize();
      if (prefersReducedMotion) {
        draw();
        return;
      }
      draw();
      window.addEventListener("resize", resize);
    };
    logo.onerror = () => {
      console.warn("[Starfield] Failed to load logo asset");
    };
    return () => {
      cancelled = true;
      cancelAnimationFrame(rafId);
      window.removeEventListener("resize", resize);
      logo.onload = null;
      logo.onerror = null;
    };
  }, [density]);
  return <canvas ref={canvasRef} aria-hidden="true" className={className} style={{
    position: "absolute",
    inset: 0,
    width: "100%",
    height: "100%",
    pointerEvents: "none",
    zIndex: 0,
    ...style
  }} {...rest} />;
};

export const BlinkingIcon = ({icon = 'terminal', size = 16, color, className = '', style = {}, ...rest}) => {
  const resolvedColor = color || 'var(--lp-color-accent)';
  return <span className={className} style={{
    display: 'inline-flex',
    ...style
  }} {...rest}>
      <style>{`
        @keyframes blink {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.3; }
        }
        @media (prefers-reduced-motion: reduce) {
          * { animation: none !important; }
        }
      `}</style>
      <span style={{
    display: 'inline-flex',
    animation: 'blink 3s ease-in-out infinite'
  }}>
        <Icon icon={icon} size={size} color={resolvedColor} />
      </span>
    </span>;
};

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 P = ({children, icon, iconSize = 16, iconColor, align = "left", gap = "var(--lp-spacing-2)", className = "", style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || "var(--p-icon-color)";
  const containerStyle = {
    display: icon ? "flex" : "block",
    alignItems: "center",
    gap: icon ? gap : 0,
    justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start",
    textAlign: align
  };
  const paragraphStyle = {
    margin: 0,
    color: "var(--lp-color-text-secondary)",
    opacity: 1
  };
  return <div className={className} style={{
    ...containerStyle,
    ...style
  }} {...rest}>
      {icon && <Icon icon={icon} size={iconSize} color={resolvedIconColor} />}
      <p style={paragraphStyle}>{children}</p>
    </div>;
};

export const H5 = ({children, icon, iconSize = 18, iconColor, align = "left", gap = "var(--lp-spacing-2)", className = "", style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || "var(--lp-color-accent)";
  const containerStyle = {
    display: icon ? "flex" : "block",
    alignItems: "center",
    gap: icon ? gap : 0,
    justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start",
    textAlign: align,
    marginBottom: "var(--lp-spacing-2)"
  };
  const headingStyle = {
    margin: 0,
    fontSize: "1.125rem",
    fontWeight: "bold",
    color: "var(--lp-color-text-primary)",
    opacity: 1
  };
  return <div className={className} style={{
    ...containerStyle,
    ...style
  }} {...rest}>
      {icon && <Icon icon={icon} size={iconSize} color={resolvedIconColor} />}
      <h5 style={headingStyle}>{children}</h5>
    </div>;
};

export const H2 = ({children, icon, iconSize = 28, iconColor, align = "left", gap = "var(--lp-spacing-3)", className = "", style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || "var(--lp-color-accent)";
  const containerStyle = {
    display: icon ? "flex" : "block",
    alignItems: "center",
    gap: icon ? gap : 0,
    justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start",
    textAlign: align,
    marginBottom: "var(--lp-spacing-4)"
  };
  const headingStyle = {
    margin: 0,
    fontSize: "1.875rem",
    fontWeight: "bold",
    color: "var(--lp-color-text-primary)",
    opacity: 1
  };
  return <div className={className} style={{
    ...containerStyle,
    ...style
  }} {...rest}>
      {icon && <Icon icon={icon} size={iconSize} color={resolvedIconColor} />}
      <h2 style={headingStyle}>{children}</h2>
    </div>;
};

export const H1 = ({children, icon, iconSize = 32, iconColor, align = "left", gap = "var(--lp-spacing-3)", className = "", style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || "var(--lp-color-accent)";
  const containerStyle = {
    display: icon ? "flex" : "block",
    alignItems: "center",
    gap: icon ? gap : 0,
    justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start",
    textAlign: align
  };
  const headingStyle = {
    margin: "2rem 0 1rem 0",
    fontSize: "2.5rem",
    fontWeight: "bold",
    lineHeight: "1.2",
    color: "var(--lp-color-text-primary)",
    opacity: 1
  };
  return <div className={className} style={{
    ...containerStyle,
    ...style
  }} {...rest}>
      {icon && <Icon icon={icon} size={iconSize} color={resolvedIconColor} />}
      <h1 style={headingStyle}>{children}</h1>
    </div>;
};

export const PortalContentContainer = ({children, className = "", style = {}, ...rest}) => {
  return <div className={["frame-mode-container", className].filter(Boolean).join(" ")} style={style} {...rest}>
            <div style={{
    display: "flex",
    flexDirection: "column",
    gap: "var(--lp-spacing-4)"
  }}>   
                {children}
            </div>
        </div>;
};

export const PortalCardsHeader = ({children, title, className = "", style = {}, ...rest}) => {
  return <div className={className} style={{
    alignContent: "center",
    justifyContent: "center",
    ...style
  }} {...rest}>
        <H2 icon="signs-post" iconSize={32}>
            {title}
        </H2>

        <div style={{
    display: "flex",
    alignItems: "center",
    gap: "var(--lp-spacing-2)",
    marginBottom: "0.1rem"
  }}>
                <span style={{
    lineHeight: "1",
    color: "var(--text-primary)",
    opacity: 1,
    fontStyle: "italic",
    fontSize: "1.2rem"
  }}>
                    Choose Your Mission:
                </span>
                {children}
        </div>
    </div>;
};

export const HeroSectionContainer = ({children, minHeight = "fit-content", className = "", style = {}, ...rest}) => {
  return <div className={["frame-mode-hero-full", className].filter(Boolean).join(" ")} style={{
    minHeight: minHeight,
    marginBottom: "var(--lp-spacing-2)",
    ...style
  }} {...rest}>
      {children}
      {}
      {}
    </div>;
};

export const HeroContentContainer = ({children, className = "", style = {}, ...rest}) => {
  return <div className={["frame-mode-container", className].filter(Boolean).join(" ")} style={{
    position: "relative",
    zIndex: 1,
    height: "100%",
    marginBottom: 0,
    ...style
  }} {...rest}>
      <div style={{
    position: "relative",
    height: "100%"
  }}>{children}</div>
    </div>;
};

export const LogoHeroContainer = ({src = "/snippets/assets/logos/Livepeer-Logo-Full-Theme.svg", alt = "Livepeer Logo", width = "100%", margin = "1rem auto 0 auto", imgHeight = "20px", imgWidth = "auto", objectFit = "contain", children, className = "", style = {}, ...rest}) => {
  return <div className={className} style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    margin: margin,
    width: width,
    paddingBottom: children ? "3.5rem" : "0",
    ...style
  }} {...rest}>
      <div style={{
    position: "relative",
    display: "inline-block"
  }}>
        <img src={src} alt={alt} style={{
    height: imgHeight,
    width: imgWidth,
    objectFit: objectFit,
    display: "block"
  }} />
        {children && <div style={{
    position: "absolute",
    top: "100%",
    right: "0",
    fontSize: "2rem",
    color: "var(--lp-color-accent)",
    fontWeight: "500",
    lineHeight: "1",
    paddingTop: "var(--lp-spacing-2)"
  }}>
            {children}
          </div>}
      </div>
    </div>;
};

export const HeroImageBackgroundComponent = ({children, className = "", style = {}, ...rest}) => {
  return <div className={className} style={{
    position: "absolute",
    inset: 0,
    overflow: "hidden",
    zIndex: 0,
    ...style
  }} {...rest}>
      {children}
      {}
    </div>;
};

export const PortalHeroContent = ({zIndex = true, title = "Portal Page", subtitle = "Build - Explore - Create", subtitleIcon = "/snippets/assets/logos/Livepeer-Logo-Symbol-Green-Theme.svg", description, refCardLink, overview, divider = true, callout = null, titleColor, subtitleColor, children, className = "", style = {}, ...rest}) => {
  return <div className={className} style={{
    position: "relative",
    zIndex: 1,
    ...style
  }} {...rest}>
      <div style={{
    textAlign: "center",
    marginTop: "var(--lp-spacing-8)",
    marginBottom: "var(--lp-spacing-4)"
  }}>
        <H1 align="center">
          {title}
        </H1>
        {subtitle && <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    gap: "var(--lp-spacing-2)"
  }}>
            {subtitleIcon && <span style={{
    marginRight: "var(--lp-spacing-2)"
  }}>
                <Icon icon={subtitleIcon} size={20} />
              </span>}
            <h2 style={{
    fontSize: "1.5rem",
    fontWeight: "500",
    opacity: 1,
    color: subtitleColor || "var(--lp-color-accent)"
  }}>
              {subtitle} {}
              {subtitleIcon && <span style={{
    display: "inline-block",
    transform: "scaleX(-1)",
    marginLeft: "var(--lp-spacing-2)"
  }}>
                  <Icon icon={subtitleIcon} size={20} />
                </span>}
            </h2>
          </div>}
        {description && <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "column",
    gap: "1rem 0",
    width: "80%",
    margin: "0 auto",
    fontSize: "1.1rem",
    color: "var(--lp-color-text-secondary)",
    paddingTop: "3rem"
  }}>
            {description}
          </div>}
        {refCardLink && <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: "fit-content",
    margin: "0 auto",
    marginTop: "var(--lp-spacing-4)"
  }}>
            {refCardLink}
          </div>}
         <div style={{
    width: "80%",
    margin: "0 auto",
    paddingBottom: "var(--lp-spacing-4)"
  }}>
          {callout && callout}
          {divider ? <CustomDivider /> : null}
          {}
        </div>
        {overview && <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "column",
    gap: "1rem 0",
    width: "80%",
    margin: "0 auto",
    fontSize: "1.1rem",
    color: "var(--lp-color-text-secondary)"
  }}>
            {overview}
          </div>}
        <div style={{
    width: "80%",
    margin: "0 auto",
    paddingTop: "var(--lp-spacing-6)",
    paddingBottom: "0.1rem"
  }}>
          {children ? <>{children}<CustomDivider /></> : <CustomDivider />}
        </div>
        </div>
      {}
    </div>;
};

{/* HeroSectionContainer - Full width of content area (excludes sidebar, adapts to sidebar collapse) */}

<HeroSectionContainer>
  <HeroImageBackgroundComponent>
    <Starfield />
  </HeroImageBackgroundComponent>

  <HeroContentContainer>
    {/* LogoHeroContainer */}

    <LogoHeroContainer imgHeight="20px" />

    {/* PortalHeroContent */}

    <PortalHeroContent
      title="Gateways"
      subtitle="Build – Create – Innovate"
      refCardLink={
          <Card
            title="go-livepeer"
            href="https://github.com/livepeer/go-livepeer"
            icon="github"
            arrow
            horizontal
          />
        }
      overview={
      <>
        Gateways serve as the primary demand aggregation layer in the Livepeer Network.
        They accept video transcoding and AI inference requests from end customers,
        then distribute these jobs across the network of GPU-equipped Orchestrators.
          <br />
          <br />
          Gateways are the core building block for those developers looking to productise applications built on top of the Livepeer Protocol.
          {/* <div style={{ fontSize: "0.9rem", fontStyle: "italic", color: "#888" }}>
              Note: In earlier documentation, Gateways were referred to as Broadcasters
          </div> */}
      </>
      }
    />
  </HeroContentContainer>
</HeroSectionContainer>

<PortalContentContainer>
  <PortalCardsHeader title="Gateway Portal">
    <BlinkingIcon icon="circle-play" size={20} />
  </PortalCardsHeader>

  <Columns cols={2}>
    <Card title={<CustomCardTitle icon="comment-question" title="What's a Gateway?" />} href="/v2/gateways/concepts/role" arrow>
      What? Where Am I? What's a Gateway? Why do I need one?

      <LinkArrow href="/v2/gateways/concepts/role" label="Concepts" />
    </Card>

    <Card title={<CustomCardTitle icon="film-canister" title="Gateway Navigator" />} href="/v2/gateways/navigator" arrow>
      A map to Gateway paths & concepts by goal, topic & journey stage.

      <LinkArrow href="/v2/gateways/navigator" label="Navigator" />
    </Card>

    <Card title={<CustomCardTitle icon="tools" title="Quickstart" />} href="/v2/gateways/quickstart/gateway-setup" arrow>
      Get started with Gateways running a local go-livepeer node for video transcoding.

      *Related:* <LinkArrow href="/v2/gateways/guides/tutorials/tutorial-1-offchain-transcoding-test" label="Full Gateway-Orchestrator Tutorial" />
    </Card>

    <Card title={<CustomCardTitle icon="rocket" title="Run a Gateway" />} href="/v2/gateways/setup/guide" arrow>
      <Badge color="green" icon="wand-magic-sparkles">
        Developer Level Up
      </Badge>

      <br />

      Deploy your own Gateway on the Livepeer Protocol with go-livepeer!
    </Card>

    <Card title={<CustomCardTitle icon="laptop-file" title="Advanced Operations & Guides" />} href="/v2/gateways/guides/tutorials/byoc-cpu-tutorial" arrow>
      Full guides, tools, troubleshooting and tutorials for running a Livepeer Gateway node.
    </Card>

    <Card title={<CustomCardTitle icon="code" title="Gateway Technical References" />} href="/v2/gateways/resources/knowledge-hub/resources" arrow>
      Find API references, protocol specs, and other technical docs related to Gateways.
    </Card>
  </Columns>

  <br />
</PortalContentContainer>
