OpenChart
Claude·Codex와 함께하는 무료 데스크톱 차트 도구. 알림 설정·차트 움직임 분석·데이터 제공자 선택
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Marketing site for Forward Matter, a Sydney software company building automated market-intelligence software for financial services and the pharmaceutical industry.
Built with Next.js (App Router) and plain CSS, from the design handoff kept in
design-handoff/ (see design-handoff/NOTES.md).
npm install
npm run dev # http://localhost:3000
npm run build # production build
npm start # serve the production build
npm run typecheck # tsc --noEmit
npm run build:static # flat, host-anywhere export in out/
out/ can be dropped on any static host, including ones that serve from a
subdirectory. It exists so the site can be shared before a real deployment, and it
is not the thing to deploy: it has no /api/contact of its own, no image optimiser,
and it pins a Google Fonts fallback for hosts that block Fontshare. Deploy
npm run build instead.
To make the preview's form actually send, point it at a deployed API:
NEXT_PUBLIC_CONTACT_ENDPOINT=https://<deployment>/api/contact npm run build:static
and add the preview's origin to CONTACT_ALLOWED_ORIGINS on the API side so the
browser's preflight passes. Without an endpoint the preview still shows the success
state — it is there to show the design — but says the enquiry was not sent, rather
than letting a real prospect believe it reached you.
/simulator is for people we have given an access phrase to. The simulator source is
kept outside this repository (place it at simulator-src/app.html, which git ignores)
and is never served as is: npm run build:simulator encrypts it once with a random key
(AES-256-GCM) and locks a copy of that key under each access phrase (PBKDF2, 200,000
rounds) into public/simulator/payload.json, which is what the page fetches. The access
phrase form tries the phrase in the visitor's browser against each locked copy and renders
the decrypted simulator in a sandboxed frame, so there is no server-side secret and it
works on the static export as well.
Each person or group can have their own phrase. The phrases are never in the repository; set them in the environment where the build runs (not in Vercel, which only ever receives the encrypted file):
SIMULATOR_PASSPHRASE='first phrase' SIMULATOR_PASSPHRASE2='second phrase' npm run build:simulator
git add public/simulator/payload.json && git commit -m "Rebuild the simulator payload"
Every variable named SIMULATOR_PASSPHRASE, SIMULATOR_PASSPHRASE2, SIMULATOR_PASSPHRASE3
and so on is used. To remove someone's access, remove their variable and rebuild. A rebuild
is a new file, not a revocation: anyone who saved an old payload.json can still open it
with the phrase that opened it then.
| Path | What it holds |
|---|---|
app/page.tsx | Home — hero, intelligence system, catalyst panel, platform, case study, closing block |
app/about/page.tsx | About us — why we exist, the team, contact CTA |
app/simulator/page.tsx | Simulator — a password-locked internal page (noindex, not in the sitemap) |
components/SimulatorGate.tsx | The access phrase form; decrypts and frames the simulator in the browser |
simulator-src/app.html | The simulator itself, kept outside the repository and ignored by git; never served in the clear |
scripts/build-simulator.mjs | Encrypts the simulator into public/simulator/payload.json |
app/api/contact/route.ts | Server-side validation and delivery for the enquiry form |
app/globals.css | Design tokens and every shared surface, button and section style |
components/ | Shell (header, footer, brand mark) and one component per home section |
content/site.ts | All copy and structured content |
public/ | Portraits, case-study photograph, the particle animation |
Copy changes belong in content/site.ts. Colour, shadow and spacing changes belong
in the token block at the top of app/globals.css.
A deliberate neumorphic treatment on a light grey ground: raised panels with a white
top-left highlight and a grey bottom-right drop shadow, recessed wells with inset
shadows, buttons that physically depress on :active, and text-shadow: 0 1px #fff
on most type. The shadow recipes are the heart of the style and live as CSS custom
properties (--shadow-card, --shadow-well, --shadow-btn and so on). The light
source is always top-left; keep that direction consistent when adding surfaces.
Every section is a repeat(auto-fit, minmax(min(100%, Npx), 1fr)) grid, so columns
collapse to one at narrow widths without media queries. Keep the min(100%, …)
inside minmax — a bare floor overflows the container instead of collapsing.
The form posts to /api/contact, which trims and validates the fields server-side
(name, firm and a well-formed email are required), drops anything that fills the
hidden honeypot field, and then delivers the enquiry — by email through Resend, or
to a webhook.
Set these in the hosting project, never in the repo:
| Variable | What it does |
|---|---|
RESEND_API_KEY | Resend key, starts re_. With CONTACT_TO, enquiries arrive as email. |
CONTACT_TO | Who the enquiry is addressed to. No default — a default would mean a real address sitting in a public repository. |
CONTACT_FROM | The from address, e.g. Forward Matter <[email protected]>. Needs a Resend-verified domain. Unset, this falls back to Resend's shared [email protected]. |
CONTACT_WEBHOOK_URL | Deliver as JSON to a CRM, Slack or Zapier hook instead of email. |
CONTACT_ALLOWED_ORIGINS | Comma-separated origins allowed to post cross-origin, for a static export hosted elsewhere. Same-origin posts never need this. |
NEXT_PUBLIC_CONTACT_ENDPOINT | Build-time only, and only for the static export: the full URL of a deployed /api/contact. |
[email protected] only delivers to the address that owns the Resend account,
so until forwardmatter.com is verified at resend.com → Domains, CONTACT_TO has
to be that address. Once it is verified, set CONTACT_FROM and CONTACT_TO can be
anything.
With none of the delivery variables set the route returns 503 and the form shows its error. It deliberately does not accept-and-log: an enquiry that vanishes while the visitor reads "Thank you" is worse than one that visibly failed.
No acknowledgement email is sent to the enquirer. Anything this endpoint mailed to a submitted address would make it a way to deliver attacker-chosen text over the Forward Matter domain, and the form already confirms receipt on the page.
aria-hidden, never takes pointer events, and is
not rendered at all when the visitor prefers reduced motion or canvas is
unavailable, leaving the static radial highlight in place.scroll-margin-top on anchored sections (so
targets clear the sticky header) are all in place.api.fontshare.com, so the
stylesheet is linked with a preconnect as the prototype does. Drop the woff2
files into public/fonts and swap the <link> in app/layout.tsx when convenient.metadataBase, the sitemap and robots use https://forwardmatter.com. Update them
if the production domain differs.