This document provides guidelines for AI agents working on the StartupOulu website codebase.
StartupOulu is a Jekyll-based static website for the startup and entrepreneurship ecosystem in Oulu, Finland. The site aggregates events, blog posts, ecosystem services, incubator programs, and mentoring resources for entrepreneurs at various stages.
main)CNAME file)_includes/header.html), Plausible is deprecated.github/
workflows/
validate-content.yml # CI: validates content and builds site on push/PR
_config.yml # Jekyll configuration (PROTECTED)
_data/
services.yaml # Ecosystem services/partners data
_events/ # Event files (YYYY-MM-slug.html)
_posts/ # Blog posts (YYYY-MM-DD-slug.markdown)
_layouts/ # Page templates
_includes/ # Reusable HTML components
events/ # Event-specific components (card, datetime, image, badge)
services/ # Service-specific components (card, filtered-list)
header.html # Head tag with analytics (PROTECTED)
footer.html # Site footer with sponsors and social links
menu.html # Navigation structure
nav.html # Navigation links
meta_tags.html # SEO meta tags
newsletter.html # Newsletter signup
_sass/ # SCSS stylesheets
_variables.scss # Colors, typography, breakpoints
_main.scss # Main import file
_components/ # Component styles (event_card, menu, newsletter, etc.)
_pages/ # Page-specific styles (home, event, service, blog, etc.)
_responsive/ # Mobile/responsive overrides
_scripts/
events.rb # Utility: convert events CSV to HTML
assets/
css/styles.scss # SCSS entry point
main.js # Site JavaScript
images/
events/ # Event cover images
blogs/ # Blog post images
contacts/ # Team member photos
mentors/ # Mentor profile images
logos/ # Logo files (SVG, PNG)
services/ # Service pages by entrepreneurship stage
ai-incubator/ # AI incubator program pages
incubator/ # Incubator program pages
mentoring/ # Mentoring program pages
oulu-startup-database/ # Startup database section
kiosk/ # Kiosk display for Samsung SmartTV
brand/ # Self-contained brand/media page (logos, colors, gradients, typeface, CSS vars)
index.html # Single-file page — all CSS inline, no Jekyll layout dependency
assets/
logos/
svg/ # SVG logo variants (naming: startupoulu_logo_{shape}_{text}_{outline}.svg)
png/ # PNG logo variants (same naming convention as SVG)
debug/ # Debug/troubleshooting pages
index.html # Homepage (layout: frontpage)
events.html # Events listing page
blogs.html # Blog listing page
archive.html # Blog archive
contact.html # Contact page
debug.html # Debug page showing all events data
ai-content.txt # AI-readable content feed (all posts/events as plain text)
events.ics # iCal feed of events
404.html # Custom 404 page
These files require explicit user approval before any modification:
_config.yml - Site-wide Jekyll configuration affecting the entire build_includes/header.html - Contains Plausible and Umami analytics tracking codesDo not modify these files without asking the user first.
All filenames in this project follow these rules:
startup-pitch.html, not Startup-Pitch.htmlmy-event.html, not my event.html or my_event.html2026-03-my-event.html, not 2026-03-my.event.htmlYYYY-MM-, blog posts use YYYY-MM-DD- or YYYY-MM-| Content type | Format | Example |
|---|---|---|
| Event | YYYY-MM-slug.html |
2026-03-startup-pitch.html |
| Blog post | YYYY-MM-DD-slug.markdown |
2026-03-15-building-bridges.markdown |
| Blog post (alt) | YYYY-MM-slug.markdown |
2026-03-building-bridges.markdown |
| Event image | YYYY-MM-descriptive-name.ext |
2026-03-startup-pitch.jpg |
| Blog image | YYYY-MM-descriptive-name.ext |
2026-03-building-bridges.png |
Location: _events/
Naming: YYYY-MM-slug.html (e.g., 2026-02-polar-bear-pitching.html)
Layout: event
---
layout: event
title: Event Title
start_time: 2026-02-15 18:00:00 # Format: YYYY-MM-DD HH:MM:SS
end_time: 2026-02-15 21:00:00 # Format: YYYY-MM-DD HH:MM:SS
location: Venue Name, Address
cover_image: event-image.jpg # Filename only (path added automatically)
cta_title: Register # Button text (optional)
cta_link: https://example.com # Button URL (optional)
excerpt: | # Short description, max 60 words (optional)
Brief event description for cards and previews.
description: | # Full event description (required)
Complete event description with all details.
---
Notes:
cover_image is just the filename; the path assets/images/events/ is prepended automatically. Do NOT use image_url — that is a deprecated field name that won’t workexcerpt is omitted, description is used for previews (truncated at 60 words)start_time and end_time must use the exact format YYYY-MM-DD HH:MM:SS (e.g., 2026-02-15 18:00:00). Omitting the time component or using non-zero-padded months (e.g., 2026-4-22) causes YAML to parse the value as an array instead of a date, which breaks sortingassets/images/events/ (recommended: 960x540px, 16:9 ratio)published: false to the front matterLocation: _posts/
Naming: YYYY-MM-DD-slug.markdown (e.g., 2026-02-06-building-bridges.markdown)
Layout: blog
---
layout: blog
title: Blog Post Title
description: Meta description for SEO and social sharing (1-2 sentences)
blog_image: /assets/images/blogs/image.jpg # Full path required
---
Post content in Markdown format...
Notes:
blog_image requires the full path starting with /assets/images/blogs/assets/images/blogs/ (recommended: 960x540px, 16:9 ratio)published: false to the front matterLocation: _data/services.yaml
- title: Service Name
description: Brief description of the service
link_label: Learn More
link_url: https://service-url.com
stage:
- just-exploring # For people exploring entrepreneurship
- getting-started # For early-stage startups
- ready-to-grow # For growth-stage companies
A service can belong to one or more stages.
Experience the Avanto: Dive In will break YAML parsing. Wrap it in quotes: title: "Experience the Avanto: Dive In"| pipe syntax for multi-line text — description and excerpt fields must use | followed by indented text on the next linesstart_time must include the time — 2026-04-22 09:00:00 works, but 2026-4-22 gets parsed as a YAML array [2026, 4, 22] and breaks the sitecover_image, not image_url — older events may use image_url but the current layouts expect cover_imageContent can be in English or Finnish. Match the language of surrounding content or ask the user if unclear.
Always ask the user before creating new content files. This includes:
_events/_posts/| Content Type | Directory | Recommended Size | Formats |
|---|---|---|---|
| Event covers | assets/images/events/ |
960x540px (16:9) | JPG, PNG |
| Blog images | assets/images/blogs/ |
960x540px (16:9) | JPG, PNG |
| Contact photos | assets/images/contacts/ |
As needed | JPG, PNG |
| Mentor photos | assets/images/mentors/ |
As needed | JPG, PNG |
Filenames must be lowercase with dashes (no spaces). Example: 2026-02-startup-pitch.jpg
| Layout | Purpose |
|---|---|
default.html |
Minimal wrapper (header only) |
frontpage.html |
Homepage with hero, nav, footer |
event.html |
Single event detail page |
events.html |
Events listing page |
blog.html |
Single blog post page |
blogs.html |
Blog listing page |
service.html |
Single service page |
services.html |
Services listing page |
contact.html |
Contact page |
plain.html |
Minimal layout for standalone pages |
post.html |
Simple post wrapper |
about.html |
About page |
jekyll-redirect-from, jekyll-feed, jekyll-sass-converter_sass/_variables.scss)#070540#FF4600#FF3198#EAEAEA800pxbrand/index.html)The brand page defines the complete identity system. Use these CSS variable names when referencing brand values:
| Name | HEX | CSS variable |
|---|---|---|
| Canvas | #EDF2F5 |
--color-canvas |
| Knowledge | #070540 |
--color-knowledge |
| Passion | #FF3296 |
--color-passion |
| Inspiration | #E9DEFF |
--color-inspiration |
| Growth | #D2FACD |
--color-growth |
| Fame | #FF4600 |
--color-fame |
Typeface: Hanken Grotesk — Black (900) for headlines, Regular (400) for body. Available on Google Fonts (fonts.google.com/specimen/Hanken+Grotesk).
Logo files follow the naming pattern startupoulu_logo_{shape}_{text-color}_text_{outline-color}_outline.{ext} and live in brand/assets/logos/svg/ and brand/assets/logos/png/.
bundle install # Install Ruby dependencies
bundle exec jekyll serve # Start local server at http://localhost:4000
A GitHub Actions workflow (.github/workflows/validate-content.yml) runs on every push to main and on pull requests. It validates:
YYYY-MM-slug.html for events, YYYY-MM-DD-slug.markdown or YYYY-MM-slug.markdown for posts)Errors appear as annotations in the Actions tab and on the commit/PR page.
Before committing changes, verify the build succeeds:
bundle exec jekyll build
This catches Liquid syntax errors, invalid YAML front matter, and broken templates.
Visit /debug/ on the live site (or debug.html locally) to see a table of all events with their parsed data. This helps verify that event front matter is correctly formatted.
ruby _scripts/events.rb # Convert events CSV to HTML format
_sass/_sass/_components/ (e.g., _event_card.scss, _menu.scss)_sass/_pages/ (e.g., _home.scss, _single_event.scss)_sass/_responsive/ (mirror page style files)_sass/_variables.scss_sass/_main.scss (imports all partials)assets/main.js_layouts/ and _includes/_includes/events/ (card, datetime, image, badge)_includes/services/ (card, filtered-list)Use short, descriptive commit messages without conventional commit prefixes:
add new startup pitch event
update services page layout
fix mobile navigation toggle
remove outdated event
Match the existing commit style in the repository history.
Before finalizing changes:
bundle exec jekyll build - must complete without errorsstart_time)start_time matches the current dateexcerpt field on events is truncated at 60 words on cards/services/events.ics) and an AI content feed (ai-content.txt)See kiosk/KIOSK.md for kiosk documentation (screens, technical constraints, how to add new screens).
If uncertain about any changes, ask the user before proceeding. This is especially important for:
_config.yml, _includes/header.html)