New Startups page (#5885)

* new startups page

* connect hubspot form

* create and use hubspot form

* hide date placeholder hack

* remove old form

* fix images

* move to /startups/apply

* update bootstrapped value

* tidied up startups page, application form

---------

Co-authored-by: Cory Watilo <cww@watilo.com>
This commit is contained in:
Eli Kinsey
2023-05-03 08:26:38 -07:00
committed by GitHub
parent c635864218
commit d46c42b3a3
10 changed files with 658 additions and 45 deletions

View File

@@ -20,7 +20,7 @@ images:
import { getImage, GatsbyImage } from 'gatsby-plugin-image'
<div class="w-[calc(100%_+_4rem)] relative -mx-4 px-8 md:-mx-16 pt-32 pb-48 -mt-32 md:-mt-44">
<div class="w-[calc(100%_+_4rem)] relative -mx-4 px-8 md:-mx-16 pt-32 pb-72 -mt-32 md:-mt-44">
<div class="md:hidden absolute top-0 left-0 w-full bottom-0 opacity-40">
<GatsbyImage image={getImage(props?.images[0])} alt="PostHog for startups" objectFit="cover" className="" />
</div>
@@ -40,18 +40,10 @@ import { getImage, GatsbyImage } from 'gatsby-plugin-image'
title="PostHog for startups"
subtitle="$50k in credits (plus extras you'll actually use) to help you get to product-market fit"
ctas={[
<CallToAction href="https://app.posthog.com/signup">Get started - free</CallToAction>
<CallToAction href="/startups/apply">Apply now</CallToAction>
]}
/>
<p class="text-center text-xs p-4 relative">
Apply for the program by following three simple steps:
<ul class="list-none">
<li>1. Create an account using the button above</li>
<li>2. Subscribe to the Scale plan on the billing page in-app</li>
<li>3. Fill in this <a href="/signup/cloud/startup">form</a> and we will review your application</li>
</ul>
</p>
</div>
@@ -97,25 +89,25 @@ import { getImage, GatsbyImage } from 'gatsby-plugin-image'
<section class="my-24 px-5">
<div class="max-w-screen-2xl mx-auto">
<h3 class="text-2xl lg:text-3xl m-0 text-center mb-6 sm:mb-16">Extra perks</h3>
<h3 class="text-2xl lg:text-3xl m-0 text-center mb-6 sm:mb-16 lg:mb-8">Extra perks</h3>
<div class="flex justify-center">
<ul class="m-0 p-0 list-none inline-grid sm:grid-cols-2 justify-evenly relative after:border-t after:border-dashed after:border-gray-accent-light after:absolute after:w-full after:left-0 after:top-1/2 after:-translateY-1/2 before:absolute before:h-full before:top-0 before:left-1/2 after:-translateX-1/2 before:border-l before:border-dashed before:border-gray-accent-light sm:after:block after:hidden sm:before:block before:hidden">
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 sm:border-b-0 border-b border-dashed border-gray-accent-light last:border-b-0">
<ul class="m-0 p-0 list-none inline-grid sm:grid-cols-2 lg:grid-cols-4 justify-evenly relative after:border-t lg:after:border-t-0 after:border-dashed after:border-gray-accent-light after:absolute after:w-full after:left-0 after:top-1/2 after:-translateY-1/2 before:absolute before:h-full before:top-0 before:left-1/2 after:-translateX-1/2 before:border-l lg:before:border-0 before:border-dashed before:border-gray-accent-light sm:after:block after:hidden sm:before:block lg:before:hidden lg:after:hidden before:hidden">
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 lg:px-8 sm:border-b-0 border-b lg:border-b-0 border-dashed border-gray-accent-light last:border-b-0">
<GatsbyImage image={getImage(props?.images[5])} alt="Startup spotlight" objectFit="contain" style={{ maxWidth: 200 }} />
<h5 class="text-xl font-extrabold m-0 pb-1 pr-4">Startup spotlight</h5>
<p class="m-0 text-[15px]">Every month, we will pick a startup of the month to spotlight. We'll do a short interview with you which we'll share on posthog.com and our socials.</p>
</li>
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 sm:border-b-0 border-b border-dashed border-gray-accent-light last:border-b-0">
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 lg:px-8 sm:border-b-0 border-b lg:border-b-0 border-dashed border-gray-accent-light last:border-b-0">
<GatsbyImage image={getImage(props?.images[6])} alt="Startup merch" objectFit="contain" style={{ maxWidth: 200 }} />
<h5 class="text-xl font-extrabold m-0 pb-1 pr-4">Free PostHog merch</h5>
<p class="m-0 text-[15px]">Because you can never have too much</p>
</li>
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 sm:border-b-0 border-b border-dashed border-gray-accent-light last:border-b-0">
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 lg:px-8 sm:border-b-0 border-b lg:border-b-0 border-dashed border-gray-accent-light last:border-b-0">
<GatsbyImage image={getImage(props?.images[7])} alt="Startup launch" objectFit="contain" style={{ maxWidth: 200 }} />
<h5 class="text-xl font-extrabold m-0 pb-1 pr-4">We'll promote your launch</h5>
<p class="m-0 text-[15px]">Doing a launch? Let us know and we'll retweet, upvote you on ProductHunt, and generally spread the word.</p>
</li>
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 sm:border-b-0 border-b border-dashed border-gray-accent-light last:border-b-0">
<li class="relative md:max-w-md py-4 md:py-8 pl-4 pr-2 md:px-12 lg:px-8 sm:border-b-0 border-b lg:border-b-0 border-dashed border-gray-accent-light last:border-b-0">
<GatsbyImage image={getImage(props?.images[8])} alt="Startup credit" objectFit="contain" style={{ maxWidth: 200 }} />
<h5 class="text-xl font-extrabold m-0 pb-1 pr-4">Extra credit</h5>
<p class="m-0 text-[15px]">Send this page to your investor or accelerator where they can apply to be a partner. If they sign up, we'll double your credits <em>and</em> send you a custom pair of PostHog AirPods.</p>
@@ -131,14 +123,14 @@ import { getImage, GatsbyImage } from 'gatsby-plugin-image'
<h3 class="text-2xl sm:text-4xl lg:text-5xl xl:text-6xl m-0 text-center mb-6 sm:mb-8">Docs & resources</h3>
<ul class="border border-dashed border-gray-accent-light p-0">
<li class="list-none px-4 py-2"><a href="/tracks">PostHog Tracks: How to use PostHog</a></li>
<li class="list-none px-4 py-2"><a href="/blog/story-about-pivots">A story about pivots</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/early-stage-analytics">The 80/20 of early-stage startup analytics</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/posthog-first-five">What we learned about hiring from our first five employees</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/how-to-run-a-transparent-company">How to run a transparent startup</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/making-something-people-want">How we made something people want</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/startup-ops-toolkit">The ops toolkit for early-stage startups</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/startup-finance-without-finance">How to run finance at your startup without hiring a finance person</a></li>
<li class="list-none px-4 py-2 border-t border-dashed border-gray-accent-light"><a href="/blog/dev-marketing-for-startups">Developer marketing for early-stage startups what weve learned</a></li>
<li class="list-none px-4 py-2 font-semibold"><a href="/blog/story-about-pivots">A story about pivots</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/early-stage-analytics">The 80/20 of early-stage startup analytics</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/posthog-first-five">What we learned about hiring from our first five employees</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/how-to-run-a-transparent-company">How to run a transparent startup</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/making-something-people-want">How we made something people want</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/startup-ops-toolkit">The ops toolkit for early-stage startups</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/startup-finance-without-finance">How to run finance at your startup without hiring a finance person</a></li>
<li class="list-none px-4 py-2 font-semibold border-t border-dashed border-gray-accent-light"><a href="/blog/dev-marketing-for-startups">Developer marketing for early-stage startups what weve learned</a></li>
</ul>
</div>
</section>
@@ -189,3 +181,10 @@ Because all of those are available if you just go to the company <a href="https:
</details>
</div>
</section>
<div className="text-center">
<h2 className="text-3xl lg:text-5xl pb-8">Ready to get started?</h2>
<CallToAction href="/startups/apply">Apply now</CallToAction>
</div>

View File

@@ -51,6 +51,7 @@ hover:text-light-yellow
justify-start
justify-start
left-16
lg:before:hidden
lg:grid-cols-1
lg:grid-cols-2
lg:grid-cols-3
@@ -65,6 +66,7 @@ lg:grid-rows-1
lg:grid-rows-2
lg:grid-rows-3
lg:grid-rows-4
lg:mb-8
lg:ml-6
lg:my-0
max-w-2xl
@@ -85,6 +87,7 @@ opacity-40
opacity-60
opacity-75
opacity-100
pb-72
pb-48
pr-32
rotate-180

21
src/api/hubspot-form.ts Normal file
View File

@@ -0,0 +1,21 @@
import { GatsbyFunctionRequest, GatsbyFunctionResponse } from 'gatsby'
import fetch from 'node-fetch'
const handler = async (req: GatsbyFunctionRequest, res: GatsbyFunctionResponse) => {
const { formID } = req.query
if (!formID) return res.status(500).send('Missing form ID')
try {
const form = await fetch(`https://api.hubapi.com/forms/v2/forms/${formID}`, {
headers: {
Authorization: `Bearer ${process.env.HUBSPOT_FORM_ACCESS_TOKEN}`,
},
}).then((res) => res.json())
return res.status(200).send(form)
} catch (err) {
console.log(err)
return res.status(500).send(err)
}
}
export default handler

View File

@@ -0,0 +1,412 @@
import { Form, Formik, useFormikContext } from 'formik'
import React, { createContext, InputHTMLAttributes, RefObject, useContext, useEffect, useRef, useState } from 'react'
import { motion } from 'framer-motion'
import { button } from 'components/CallToAction'
import { useLocation } from '@reach/router'
import Confetti from 'react-confetti'
import { animateScroll as scroll } from 'react-scroll'
interface CustomFieldOption {
label: string
value: string | number
}
interface IProps {
formID: string
validationSchema?: any
customMessage?: React.ReactNode
customFields?: {
[key: string]: {
type: 'radioGroup'
options: CustomFieldOption[]
}
}
}
export interface Form {
portalId: number
guid: string
name: string
action: string
method: string
cssClass: string
redirect: string
submitText: string
followUpId: string
notifyRecipients: string
leadNurturingCampaignId: string
formFieldGroups: FormFieldGroup[]
createdAt: number
updatedAt: number
performableHtml: string
migratedFrom: string
ignoreCurrentValues: boolean
metaData: MetaDatum[]
deletable: boolean
inlineMessage: string
tmsId: string
captchaEnabled: boolean
campaignGuid: string
cloneable: boolean
editable: boolean
formType: string
deletedAt: number
themeName: string
parentId: number
style: string
isPublished: boolean
publishAt: number
unpublishAt: number
publishedAt: number
customUid: string
createMarketableContact: boolean
editVersion: number
thankYouMessageJson: string
themeColor: string
alwaysCreateNewCompany: boolean
internalUpdatedAt: number
businessUnitId: number
portableKey: string
paymentSessionTemplateIds: any[]
selectedExternalOptions: any[]
embedVersion: string
}
export interface FormFieldGroup {
fields: Field[]
default: boolean
isSmartGroup: boolean
richText: RichText
isPageBreak: boolean
}
export interface Field {
name: string
label: string
type: string
fieldType: string
description: string
groupName: string
displayOrder: number
required: boolean
selectedOptions: any[]
options: any[]
validation: Validation
enabled: boolean
hidden: boolean
defaultValue: string
isSmartField: boolean
unselectedLabel: string
placeholder: string
dependentFieldFilters: any[]
labelHidden: boolean
propertyObjectType: string
metaData: MetaDatum[]
objectTypeId: string
}
export interface MetaDatum {
name: string
value: string
}
export interface Validation {
name: string
message: string
data: string
useDefaultBlockList: boolean
blockedEmailAddresses: any[]
}
export interface RichText {
content: string
type: string
}
const FormContext = createContext<{
fields: Field[]
openOptions: string[]
setOpenOptions: React.Dispatch<React.SetStateAction<string[]>>
}>({
fields: [],
openOptions: [],
setOpenOptions: () => null,
})
function Radio({
value,
label,
name,
reference,
}: {
value: string | number
label: string
name: string
reference?: RefObject<HTMLInputElement>
}) {
const { fields, openOptions, setOpenOptions } = useContext(FormContext)
const { setFieldValue, values } = useFormikContext()
const handleChange = async (e: React.FormEvent<HTMLInputElement>) => {
const { value } = e.currentTarget
name && value && (await setFieldValue(name, value))
}
const handleClick = () => {
const nextIndex = fields.findIndex((field) => field.name === name) + 1
const nextField = fields[nextIndex]
if (nextField && typeof window !== 'undefined') {
const nextName = nextField?.name
const nextValue =
nextField?.options && (nextField?.options[0]?.value || nextField?.options[0]?.hubspotValue)
const nextID = `${nextName}${nextValue ? '-' + nextValue : ''}`
!openOptions.includes(nextName) && setOpenOptions([...openOptions, nextName])
const nextEl = document.getElementById(nextID)
if (!values[nextName]) {
setTimeout(() => {
nextEl?.focus()
setFieldValue(nextName, nextValue || '')
}, 0)
}
}
}
return (
<label
onMouseUp={handleClick}
className="relative w-full text-center cursor-pointer"
htmlFor={`${name}-${value}`}
>
<input
checked={values[name] == value}
className="absolute opacity-0 peer inset-0"
type="radio"
value={value}
onChange={handleChange}
id={`${name}-${value}`}
{...(reference ? { ref: reference } : {})}
/>
<span className="block py-2 w-full rounded-md border-[2px] border-black/10 peer-focus:border-black/40 peer-checked:!border-black/80 text-sm">
{label}
</span>
</label>
)
}
function RadioGroup({
options,
name,
placeholder,
}: {
options: CustomFieldOption[]
name: string
placeholder: string
}) {
if (!name) return null
const { openOptions, setOpenOptions } = useContext(FormContext)
const { errors, values, setFieldValue } = useFormikContext()
const error = errors[name]
const open = openOptions.includes(name)
const ref = useRef<HTMLInputElement>(null)
return (
<div
onFocus={() => {
!openOptions.includes(name) && setOpenOptions([...openOptions, name])
}}
onClick={() => {
if (options && !openOptions.includes(name)) {
setOpenOptions([...openOptions, name])
if (!values[name]) {
ref.current?.focus()
setFieldValue(name, options[0]?.value)
}
}
}}
className={`${inputContainerClasses} ${error ? 'pb-8' : ''} cursor-pointer`}
>
<p className={`m-0 ${open ? 'text-sm opacity-100' : 'opacity-50'} transition-all`} id={`group-${name}`}>
{placeholder}
</p>
<motion.div className="overflow-hidden" animate={{ height: open ? 'auto' : 0 }} initial={{ height: 0 }}>
<p className="m-0 mt-1 mb-4 text-xs">
<strong>Tip:</strong> Use{' '}
<kbd
className="text-xs border border-b-2 border-gray-accent-light/50 dark:border-gray-accent-dark/50 rounded-sm px-1.5 py-0.5 text-black/40 dark:text-white/40 font-sans mr-1"
style={{ fontSize: '10px' }}
>
</kbd>
<kbd
className="text-xs border border-b-2 border-gray-accent-light/50 dark:border-gray-accent-dark/50 rounded-sm px-1.5 py-0.5 text-black/40 dark:text-white/40 font-sans"
style={{ fontSize: '10px' }}
>
</kbd>{' '}
to advance through options
</p>
<div
role="radiogroup"
aria-labelledby={`group-${name}`}
className={`mt-2 grid grid-cols-2 gap-x-2 gap-y-2 ${open ? 'opacity-100' : 'opacity-0 absolute'}`}
>
{options?.map((option, index) => {
const { value, label } = option
return (
<Radio
{...(index === 0 && ref ? { reference: ref } : {})}
key={value}
value={value}
label={label}
name={name}
/>
)
})}
</div>
</motion.div>
{error && <p className="text-red font-semibold m-0 text-sm absolute bottom-1">{error}</p>}
</div>
)
}
const inputContainerClasses = `p-4 bg-tan group active:bg-white focus-within:bg-white relative text-left`
const Input = (props: InputHTMLAttributes<HTMLInputElement>) => {
const { name, placeholder } = props
if (!name) return null
const [type, setType] = useState('text')
const { errors, validateField, setFieldValue } = useFormikContext()
const error = errors[name]
return (
<label className={`${inputContainerClasses} ${error ? 'pb-8' : ''}`} htmlFor={props.id}>
<input
onChange={(e) => setFieldValue(name, e.target.value)}
onBlur={() => {
validateField(name)
setType('text')
}}
className={`bg-transparent w-full outline-none absolute left-0 px-4 ${
error ? 'bottom-6 placeholder-shown:bottom-8' : 'bottom-2 placeholder-shown:bottom-4'
} peer placeholder-shown:placeholder-transparent transition-all border-0 py-0 shadow-none ring-0 focus:ring-0`}
{...props}
onFocus={() => setType(props.type ?? 'text')}
type={type}
/>
<span className="relative -top-3 peer-placeholder-shown:top-0 text-xs peer-placeholder-shown:text-base peer-placeholder-shown:opacity-50 transition-all">
{placeholder}
</span>
{error && <p className="text-red font-semibold m-0 text-sm absolute bottom-1">{error}</p>}
</label>
)
}
export default function HubSpotForm({ formID, customFields, customMessage, validationSchema }: IProps) {
const { href } = useLocation()
const [openOptions, setOpenOptions] = useState<string[]>([])
const [form, setForm] = useState<{ fields: Field[]; buttonText: string; message: string }>({
fields: [],
buttonText: '',
message: '',
})
const [submitted, setSubmitted] = useState(false)
const [confetti, setConfetti] = useState(true)
const handleSubmit = async (values) => {
const submission = {
pageUri: href,
fields: form.fields.map(({ name, objectTypeId }) => {
const value = values[name]
return {
objectTypeId,
name,
value,
}
}),
}
const res = await fetch(`https://api.hsforms.com/submissions/v3/integration/submit/6958578/${formID}`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(submission),
}).catch((err) => {
console.log(err)
return err
})
if (res.status === 200) {
setSubmitted(true)
scroll.scrollToTop()
}
}
useEffect(() => {
fetch(`/api/hubspot-form?formID=${formID}`)
.then((res) => res.json())
.then((form: Form) => {
const fields = form.formFieldGroups
.map((group) => {
return group.fields
})
.flat()
setForm({
fields,
buttonText: form.submitText,
message: form.inlineMessage,
})
})
}, [])
return form.fields.length > 0 ? (
submitted ? (
<>
{confetti && (
<div className="fixed inset-0">
<Confetti onConfettiComplete={() => setConfetti(false)} recycle={false} numberOfPieces={1000} />
</div>
)}
<div className="bg-gray-accent-light px-6 py-8 rounded-md mt-4">
{customMessage || <p>{form.message}</p>}
</div>
</>
) : (
<FormContext.Provider value={{ fields: form.fields, openOptions, setOpenOptions }}>
<Formik
validateOnChange={false}
validationSchema={validationSchema}
initialValues={Object.fromEntries(form.fields.map(({ name }) => [name, '']))}
onSubmit={handleSubmit}
>
<Form>
<div className="grid divide-y divide-dashed divide-gray-accent-light border border-gray-accent-light border-dashed">
{form.fields.map(({ name, label, type, required }, index) => {
if (customFields && customFields[name])
return {
radioGroup: (
<RadioGroup
options={customFields[name].options}
name={name}
placeholder={label}
/>
),
}[customFields[name]?.type]
return (
<Input
key={`${name}-${index}`}
type={type === 'string' ? 'text' : type}
name={name}
placeholder={label}
required={required}
/>
)
})}
</div>
<button className={button(undefined, 'full', 'mt-4', 'sm')} type="submit">
{form.buttonText}
</button>
</Form>
</Formik>
</FormContext.Provider>
)
) : null
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,171 @@
import { Check2 } from 'components/Icons'
import Layout from 'components/Layout'
import { GatsbyImage, getImage, StaticImage } from 'gatsby-plugin-image'
import React from 'react'
import * as Yup from 'yup'
import { graphql, useStaticQuery } from 'gatsby'
import Link from 'components/Link'
import SEO from 'components/seo'
import HubSpotForm from 'components/HubSpotForm'
const benefits = [
'A year of PostHog',
'Free PostHog merch',
'$50,000 in PostHog credit',
'Private office hours',
'Startup spotlight',
'Opportunities for extra credits',
]
const validationSchema = Yup.object().shape({
firstname: Yup.string().required('Please enter your first name'),
lastname: Yup.string().required('Please enter your last name'),
email: Yup.string().email('Please enter a valid email address').required('Please enter a valid email address'),
name: Yup.string().required('Please enter your company name'),
domain: Yup.string().required('Please enter your company name'),
self_registration_organization_name: Yup.string().required('Please enter your company name'),
self_registration_raised: Yup.number().required('Please select a value'),
self_registration_company_founded: Yup.string().required('Please enter a date'),
})
const Spotlight = ({ frontmatter: { title, featuredImage }, excerpt, fields: { slug } }) => {
return (
<div className="p-4 border border-gray-accent-light rounded-md max-w-sm">
<h4>{title}</h4>
<GatsbyImage className="rounded-md" image={getImage(featuredImage)} />
<p className="my-4 text-[15px]">{excerpt}</p>
<Link to={slug} external>
Read the full story
</Link>
</div>
)
}
export default function Startups() {
const { spotlight } = useStaticQuery(graphql`
{
spotlight: mdx(fields: { slug: { eq: "/blog/startup-tigris" } }) {
frontmatter {
title
featuredImage {
childImageSharp {
gatsbyImageData
}
}
}
fields {
slug
}
excerpt(pruneLength: 200)
}
}
`)
return (
<Layout>
<SEO title={'Startups - PostHog'} />
<section className="text-center py-20 max-w-screen-lg mx-auto relative px-5">
<div className="absolute right-0 -bottom-12 md:-bottom-20 md:max-w-[240px] max-w-[150px]">
<StaticImage width={240} src="./images/belay-on.png" />
</div>
<div className="absolute left-0 bottom-0 max-w-[120px] md:max-w-[200px]">
<StaticImage width={200} src="./images/on-belay.png" />
</div>
<div className="relative hidden lg:block">
<h1 className="max-w-lg mx-auto pb-2 text-center">Apply for PostHog's startup program</h1>
<div className="max-w-sm rounded p-4 text-left bg-gray-accent-light mx-auto">
<h3 className="text-lg mb-1">How to apply:</h3>
<ol>
<li>
<Link to="https://app.posthog.com/signup" externalNoIcon>
Sign up
</Link>{' '}
for PostHog Cloud
</li>
<li>
Visit the billing page and upgrade to the <em>Paid</em> plan
</li>
<li>Complete the application below</li>
</ol>
</div>
</div>
</section>
<div className="relative lg:hidden -mb-12">
<h1 className="max-w-lg mx-auto pb-2 text-center">Apply for PostHog's startup program</h1>
<div className="max-w-sm rounded p-4 text-left bg-gray-accent-light mx-auto">
<h3 className="text-lg mb-1">How to apply:</h3>
<ol>
<li>
<Link to="https://app.posthog.com/signup" externalNoIcon>
Sign up
</Link>{' '}
for PostHog Cloud
</li>
<li>
Visit the billing page and upgrade to the <em>Paid</em> plan
</li>
<li>Complete the application below</li>
</ol>
</div>
</div>
<section className="grid md:grid-cols-2 gap-y-8 md:gap-y-0 md:gap-x-12 max-w-[1100px] mx-auto px-5 my-24">
<div>
<h4>Benefits of joining</h4>
<ul className="list-none p-0 m-0 grid grid-flow-row md:grid-cols-2 gap-y-4">
{benefits.map((benefit) => {
return (
<li className="flex items-start space-x-2" key={benefit}>
<Check2 className="w-4 opacity-57 flex-shrink-0 mt-[2px]" />
<span className="leading-tight text-[15px]">{benefit}</span>
</li>
)
})}
</ul>
<div className="mt-6 md:mt-8">
<Spotlight {...spotlight} />
</div>
</div>
<div className="flex-shrink-0">
<h3 className="mb-0">Finish your application</h3>
<p>Remember to complete the steps listed above!</p>
<HubSpotForm
validationSchema={validationSchema}
formID="aa91765b-e790-4e90-847e-46c7ebf43705"
customMessage={
<>
<h4>
<strong>Application received!</strong>
</h4>
<p>We'll get back to you once we've had a chance to review your information.&nbsp;</p>
<p>
<strong>Reminder:</strong> If you haven't signed up for PostHog yet, be sure to
follow steps 1-2 above!
</p>
<p className="mb-0">
In the meantime, why not join <Link to="/slack">our Slack community</Link>?
</p>
</>
}
customFields={{
self_registration_raised: {
type: 'radioGroup',
options: [
{ label: 'Boostrapped', value: 0 },
{ label: 'Under $100k', value: 100_000 },
{ label: '$100k - $500k', value: 500_000 },
{ label: '$500k - $1m', value: 1_000_000 },
{ label: '$1m - $5m', value: 5_000_000 },
{ label: 'More than $5m', value: 100_000_000_000 },
],
},
}}
/>
</div>
</section>
</Layout>
)
}

View File

@@ -1,4 +1,4 @@
// AUTO GENERATED FILE
// AUTO GENERATED FILE
import { ArrayCTA } from './components/ArrayCTA'
import { BasicHedgehogImage } from './components/BasicHedgehogImage'
@@ -9,6 +9,7 @@ import { CompensationCalculator } from './components/CompensationCalculator'
import { FeatureAvailability } from './components/FeatureAvailability'
import { GDPRForm } from './components/GDPRForm'
import { HiddenSection } from './components/HiddenSection'
import { HubSpotForm } from './components/HubSpotForm'
import { LPCTA } from './components/LPCTA'
import { NewsletterTutorial } from './components/NewsletterTutorial'
import { OverflowXSection } from './components/OverflowXSection'
@@ -17,25 +18,28 @@ import { ProductLayout } from './components/ProductLayout'
import { Quote2 } from './components/Quote2'
import { Squeak } from './components/Squeak'
import { StarRepoButton } from './components/StarRepoButton'
import { Startups } from './components/Startups'
import { TracksCTA } from './components/TracksCTA'
export const shortcodes = {
ArrayCTA,
BasicHedgehogImage,
BorderWrapper,
CallToAction,
Caption,
CompensationCalculator,
FeatureAvailability,
GDPRForm,
HiddenSection,
LPCTA,
NewsletterTutorial,
OverflowXSection,
Quote,
ProductLayout,
Quote2,
Squeak,
StarRepoButton,
TracksCTA
}
ArrayCTA,
BasicHedgehogImage,
BorderWrapper,
CallToAction,
Caption,
CompensationCalculator,
FeatureAvailability,
GDPRForm,
HiddenSection,
HubSpotForm,
LPCTA,
NewsletterTutorial,
OverflowXSection,
Quote,
ProductLayout,
Quote2,
Squeak,
StarRepoButton,
Startups,
TracksCTA,
}

View File

@@ -0,0 +1,3 @@
import Startups from 'components/Startups'
export default Startups

View File

@@ -213,7 +213,7 @@ h4 {
@apply p-2 pl-4 cursor-pointer border-gray-accent-light dark:border-gray-accent-dark border-dashed border;
> summary {
@apply text-red list-none pl-4 relative before:absolute before:left-[3px] before:top-[8px] before:w-[10px] before:h-[7px];
@apply text-red font-semibold list-none pl-4 relative before:absolute before:left-[3px] before:top-[8px] before:w-[10px] before:h-[7px];
&:before {
background: url("data:image/svg+xml,%3Csvg width='10' height='7' viewBox='0 0 10 7' fill='none' xmlns='http://www.w3.org/2000/svg'%0A%3E%3Cpath d='M8.15448 0.316976L5.00049 3.47201L1.8465 0.316976C1.42387 -0.105659 0.73923 -0.105659 0.316596 0.316976C-0.105532 0.739104 -0.105532 1.42425 0.316596 1.84636L4.23586 5.76563C4.65799 6.18726 5.34211 6.18726 5.76421 5.76563L9.68296 1.84688V1.84637C10.1056 1.42425 10.1056 0.740128 9.68347 0.317507C9.26134 -0.105115 8.57722 -0.105128 8.1546 0.317L8.15448 0.316976Z' fill='%23EF7632' /%3E%3C/svg%3E");