From afd461b5b7258bb0f38af238062c762bd43d231f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Portela=20Afonso?= Date: Thu, 16 Jul 2026 11:08:47 +0100 Subject: [PATCH] feat: convert site to jekyll --- .github/workflows/build-deploy-jekyll.yml | 52 ++++ Gemfile | 11 + _config.yml | 16 ++ _includes/cookie-logo.html | 4 + _includes/footer.html | 14 ++ _includes/nav.html | 12 + _layouts/default.html | 22 ++ _layouts/post.html | 27 +++ _posts/2026-06-30-chaos-engineering-rocky.md | 35 +++ .../2026-07-07-home-assistant-automations.md | 41 ++++ _posts/2026-07-14-hello-world.md | 31 +++ assets/css/blog.css | 62 +++++ assets/css/home.css | 106 +++++++++ assets/css/shared.css | 30 +++ blog/index.html | 39 +++ index.html | 223 +----------------- 16 files changed, 512 insertions(+), 213 deletions(-) create mode 100644 .github/workflows/build-deploy-jekyll.yml create mode 100644 Gemfile create mode 100644 _config.yml create mode 100644 _includes/cookie-logo.html create mode 100644 _includes/footer.html create mode 100644 _includes/nav.html create mode 100644 _layouts/default.html create mode 100644 _layouts/post.html create mode 100644 _posts/2026-06-30-chaos-engineering-rocky.md create mode 100644 _posts/2026-07-07-home-assistant-automations.md create mode 100644 _posts/2026-07-14-hello-world.md create mode 100644 assets/css/blog.css create mode 100644 assets/css/home.css create mode 100644 assets/css/shared.css create mode 100644 blog/index.html diff --git a/.github/workflows/build-deploy-jekyll.yml b/.github/workflows/build-deploy-jekyll.yml new file mode 100644 index 0000000..a425514 --- /dev/null +++ b/.github/workflows/build-deploy-jekyll.yml @@ -0,0 +1,52 @@ +name: Deploy Jekyll with GitHub Pages dependencies preinstalled + +on: + # Runs on pushes targeting the default branch + push: + branches: + - main + - feat/jekyll + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./ + destination: ./_site + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..8526702 --- /dev/null +++ b/Gemfile @@ -0,0 +1,11 @@ +source "https://rubygems.org" + +gem "jekyll", "~> 4.3" + +# Uncomment instead if deploying via GitHub Pages' built-in build: +# gem "github-pages", group: :jekyll_plugins + +platforms :mingw, :x64_mingw, :mswin, :jruby do + gem "tzinfo", ">= 1", "< 3" + gem "tzinfo-data" +end diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..9afc437 --- /dev/null +++ b/_config.yml @@ -0,0 +1,16 @@ +title: CookieBytes +description: Software that's baked to perfection. CookieBytes builds digital products with warmth, precision, and a little love. +url: "https://cookiebytes.pt" +baseurl: "" +lang: en + +permalink: /blog/:title/ + +markdown: kramdown +kramdown: + input: GFM + +exclude: + - Gemfile + - Gemfile.lock + - README.md diff --git a/_includes/cookie-logo.html b/_includes/cookie-logo.html new file mode 100644 index 0000000..dac7322 --- /dev/null +++ b/_includes/cookie-logo.html @@ -0,0 +1,4 @@ +{% assign s = include.size | default: 32 %} + + + diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000..6964aa7 --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,14 @@ + diff --git a/_includes/nav.html b/_includes/nav.html new file mode 100644 index 0000000..e5770e1 --- /dev/null +++ b/_includes/nav.html @@ -0,0 +1,12 @@ + diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..02b73e5 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,22 @@ + + + + + +{% if page.title %}{{ page.title }} · {{ site.title }}{% else %}{{ site.title }}{% endif %} + + + + +{% if page.extra_css %}{% for css in page.extra_css %} +{% endfor %}{% endif %} + + +{% include nav.html %} + +{{ content }} + +{% include footer.html %} + + + diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 0000000..129e16e --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,27 @@ +--- +layout: default +extra_css: + - /assets/css/blog.css +--- +
+ ← Back to the Cookie Jar + +

{{ page.title }}

+ {% if page.lede %}

{{ page.lede }}

{% endif %} + +
+ {{ content }} +
+ +
+
{{ page.author_emoji | default: '🍪' }}
+
+ The CookieBytes team + {{ page.author_note }} +
+
+
diff --git a/_posts/2026-06-30-chaos-engineering-rocky.md b/_posts/2026-06-30-chaos-engineering-rocky.md new file mode 100644 index 0000000..df20ef8 --- /dev/null +++ b/_posts/2026-06-30-chaos-engineering-rocky.md @@ -0,0 +1,35 @@ +--- +layout: post +title: What Rocky taught us about chaos engineering +description: Our Head of Chaos Engineering has deleted two Figma files and one production config. Here's how a cat made our deployment pipeline genuinely more resilient. +category: crew +emoji: 🐱 +read_time: 5 min read +lede: Netflix has Chaos Monkey. We have a cat. Rocky's job title — Head of Chaos Engineering — started as a joke after he deleted a production config by sleeping on a keyboard. Then we realized he'd found a real gap, and the joke became a discipline. +author_emoji: 🐱 +author_note: Reviewed by Rocky, who walked across the draft twice and deleted a paragraph. It was probably the weakest one. +--- + +## The incident + +A Tuesday afternoon. Rocky settles onto a laptop, as cats do. Twelve minutes later, a config file in a production repo has been overwritten with what can only be described as `jjjjjjjjjjjjj4`, committed (autosave plus a very unfortunate keyboard shortcut), and deployed by our pipeline — which validated nothing. + +The service went down. The rollback took 40 minutes because we'd never actually rehearsed one. Rocky slept through the entire recovery. + +## What the cat exposed + +Every part of that failure was ours, not his. The pipeline deployed config changes without schema validation. Nothing alerted us until users did. And our rollback was a wiki page last updated two quarters earlier. Rocky didn't break the system — he revealed it was already broken, cheaper than a real outage with a real client watching. + +
🐾 Chaos engineering in one sentence: if a cat on a keyboard can take down production, production was the problem, not the cat.
+ +## What changed + +Config files now pass schema validation before any deploy — a malformed value fails the pipeline, not the service. Deploys are staged with automatic rollback on failed health checks. And once a month we run a "Rocky drill": someone intentionally breaks a staging environment in an unannounced way, and we measure how long detection and recovery take. The first drill took us 35 minutes to even notice. We're now under four. + +## The honest lesson + +Most small teams skip resilience work because it never feels urgent — until the outage, when it's suddenly the only thing that matters. You don't need Netflix-scale tooling. You need validation gates, rehearsed rollbacks, and something unpredictable applying pressure. We happen to have that last part covered in-house, ginger, and fundamentally unrepentant. + +
🍪 🍪 🍪
+ +Want your pipeline cat-proofed? [Talk to us](/#contact) — DevOps audits are one of our favorite jobs, and yes, Rocky consults. diff --git a/_posts/2026-07-07-home-assistant-automations.md b/_posts/2026-07-07-home-assistant-automations.md new file mode 100644 index 0000000..0b7899b --- /dev/null +++ b/_posts/2026-07-07-home-assistant-automations.md @@ -0,0 +1,41 @@ +--- +layout: post +title: Five Home Assistant automations we install in every smart home +description: After dozens of installs, these are the automations that clients actually keep using six months later — plus the ones we quietly stopped recommending. +category: tech +emoji: 🏠 +read_time: 7 min read +lede: After dozens of Home Assistant installs, a pattern emerged — clients love automations for two weeks, then quietly disable most of them. These five are the ones that survive. They're boring, and that's exactly the point. +author_emoji: 🏠 +author_note: Field-tested in real homes, including our own — where the leak sensor's first catch was Billy's water bowl. +--- + +## 1. Lights that follow the sun, not a schedule + +Fixed-time lighting schedules break twice a year and feel wrong most evenings. Instead, we trigger evening lights on `sun.sun` elevation — typically when the sun drops below 1.5° — and ramp brightness gradually. Nobody ever notices this automation, which is the highest compliment an automation can get. + +## 2. The "everyone left" reset + +One automation, triggered when the last person's presence drops: lights off, thermostat to eco, media players paused, and a notification only if a window was left open. We use person entities grouped into a `zone.home` count rather than device trackers directly — phones lie, and Wi-Fi presence flaps when someone's phone sleeps. + +
💡 The key detail: a 10-minute delay with a cancel condition. Without it, taking out the trash turns your house off.
+ +## 3. Morning modes, not morning alarms + +Instead of "at 7:00 do X", we set up an `input_select` for house mode (sleeping, waking, day, evening, away). Automations react to the mode change, and the mode itself can be flipped by time, first motion in the kitchen, or manually from a dashboard. Decoupling "what happens" from "when it happens" is the single biggest maintainability win in Home Assistant. + +## 4. Water and leak alerts that escalate + +Leak sensors under the sink, behind the washing machine, near the water heater. First alert: phone notification. No acknowledgment in 5 minutes: notification to everyone in the house plus lights flash red. If a smart valve is installed, shut the water after 10. The escalation ladder matters — a single silent notification at 3 a.m. protects nothing. + +## 5. The "is everything okay?" nightly digest + +At 22:00 the house sends one message: doors locked or not, windows open or not, batteries below 15%, and any entity that's been `unavailable` for over an hour. One message, once a day. This replaces the dozen nagging notifications that make people mute the app entirely. + +
🍪 🍪 🍪
+ +## What we stopped recommending + +Motion-triggered lights in living rooms (great in hallways, infuriating on movie night), voice announcements for routine events (novelty wears off in days), and any automation that requires the client to remember it exists. If it needs a manual, it's a feature, not an automation. + +Thinking about a Home Assistant setup? [We do installs](/#contact) — sensors, dashboards, escalation ladders and all. diff --git a/_posts/2026-07-14-hello-world.md b/_posts/2026-07-14-hello-world.md new file mode 100644 index 0000000..dd093a7 --- /dev/null +++ b/_posts/2026-07-14-hello-world.md @@ -0,0 +1,31 @@ +--- +layout: post +title: Hello, world — the oven is officially open +description: Why a small software studio decided to start writing things down — what we're building, what we've learned, and why the blog is named after a cookie jar. +category: studio +emoji: 🍪 +read_time: 4 min read +lede: Every software studio eventually writes a "hello world" blog post. This is ours. It only took us a year, three rebrands (thanks, Billy), and one cat-related data loss incident to get here. +author_emoji: 🍪 +author_note: Written by humans. Reviewed by Peggy, who said nothing, which we're taking as approval. +--- + +## Why a blog? + +We spend our days building mobile apps, APIs, infrastructure, and smart homes for other people. Along the way we accumulate opinions — about tools that are better than their documentation suggests, patterns that quietly save projects, and automations that clients actually use instead of politely ignoring. + +Until now, those opinions lived in Slack threads and post-mortems. The Cookie Jar is where we'll put them instead: honest write-ups of what worked, what didn't, and what Rocky deleted along the way. + +## What to expect + +Three kinds of posts, roughly. **Tech** posts are the practical ones — Home Assistant setups, backend patterns, DevOps lessons from real projects. **Studio** posts are about how we work: process, tooling, the occasional strong opinion about estimates. And **Crew** posts are about the four animals who supervise everything we ship, because frankly they generate more content than the codebase does. + +We aim for a post every couple of weeks. No SEO filler, no "10 reasons why" listicles, no AI-generated fluff about synergy. If we publish it, it's because we'd want to read it. + +
🍪 🍪 🍪
+ +## The name + +The Cookie Jar felt right. It's where the good stuff is kept, everyone sneaks into it when nobody's watching, and Cookie — the original good girl this whole studio is named after — strongly approves of anything jar-adjacent. + +Got a topic you'd like us to cover? [Email us](mailto:hello@cookiebytes.pt). If it's a good one, we'll bake it. diff --git a/assets/css/blog.css b/assets/css/blog.css new file mode 100644 index 0000000..51f53b5 --- /dev/null +++ b/assets/css/blog.css @@ -0,0 +1,62 @@ +/* CookieBytes — blog listing + article styles */ + +/* Listing */ +.blog-hero { padding: 9rem 2rem 3.5rem; text-align: center; position: relative; overflow: hidden; } +.blog-hero-bg { position: absolute; inset: 0; z-index: 0; background: radial-gradient(ellipse 80% 60% at 50% 110%, rgba(201,123,58,0.10) 0%, transparent 70%), radial-gradient(circle at 85% 15%, rgba(38,168,141,0.07) 0%, transparent 35%); } +.blog-hero > * { position: relative; z-index: 1; } +.blog-label { font-family: 'DM Mono', monospace; font-size: 0.75rem; font-weight: 500; color: var(--cookie); letter-spacing: 0.15em; text-transform: uppercase; margin-bottom: 0.9rem; animation: fadeSlideIn 0.7s 0.05s ease both; } +.blog-hero h1 { font-family: 'Fraunces', serif; font-size: clamp(2.6rem, 6vw, 4.5rem); font-weight: 900; letter-spacing: -0.03em; color: var(--chip); line-height: 1.05; margin-bottom: 1.1rem; animation: fadeSlideIn 0.7s 0.15s ease both; } +.blog-hero h1 em { font-style: normal; color: var(--cookie); } +.blog-hero p { font-size: 1.05rem; font-weight: 300; color: var(--text-muted); max-width: 480px; margin: 0 auto; line-height: 1.65; animation: fadeSlideIn 0.7s 0.25s ease both; } + +.posts-wrap { max-width: 860px; margin: 0 auto; padding: 1rem 2rem 6rem; display: flex; flex-direction: column; gap: 1.5rem; } +.post-card { display: block; background: var(--soft-white); border: 1px solid var(--border); border-radius: 1.75rem; padding: 2.2rem 2.4rem; text-decoration: none; color: inherit; transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; position: relative; overflow: hidden; } +.post-card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(201,123,58,0.12); border-color: var(--cookie-light); } +.post-card h2 { font-family: 'Fraunces', serif; font-weight: 700; font-size: 1.55rem; color: var(--chip); letter-spacing: -0.02em; line-height: 1.2; margin-bottom: 0.6rem; } +.post-card p.excerpt { font-size: 0.92rem; color: var(--text-muted); line-height: 1.65; max-width: 640px; } +.post-arrow { display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 1.1rem; font-size: 0.85rem; font-weight: 600; color: var(--cookie); transition: gap 0.2s; } +.post-card:hover .post-arrow { gap: 0.7rem; } +.post-emoji { position: absolute; font-size: 6.5rem; opacity: 0.07; top: -1rem; right: 0.5rem; pointer-events: none; transform: rotate(12deg); } +@media (max-width: 560px) { .post-card { padding: 1.7rem 1.5rem; } } + +/* Meta (shared listing + article) */ +.post-meta { display: flex; align-items: center; gap: 0.9rem; margin-bottom: 0.9rem; flex-wrap: wrap; } +.post-tag { font-family: 'DM Mono', monospace; font-size: 0.65rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; padding: 0.28rem 0.85rem; border-radius: 1rem; } +.tag-studio { background: #FDF0E0; color: var(--cookie-dark); } +.tag-tech { background: var(--byte-light); color: var(--byte); } +.tag-crew { background: #F3E8FB; color: var(--rocky); } +.post-date, .post-read { font-family: 'DM Mono', monospace; font-size: 0.72rem; color: var(--text-muted); } + +/* Subscribe strip */ +.subscribe-strip { max-width: 860px; margin: 0 auto 6rem; padding: 0 2rem; } +.subscribe-box { background: var(--byte); border-radius: 2rem; padding: 2.4rem 2.5rem; display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; } +.subscribe-box h3 { font-family: 'Fraunces', serif; font-weight: 900; font-size: 1.4rem; color: white; margin-bottom: 0.3rem; } +.subscribe-box p { font-size: 0.88rem; color: rgba(255,255,255,0.7); } +.subscribe-btn { background: white; color: var(--byte); padding: 0.75rem 1.8rem; border-radius: 3rem; font-size: 0.9rem; font-weight: 600; text-decoration: none; white-space: nowrap; transition: transform 0.15s, box-shadow 0.2s; } +.subscribe-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); } + +/* Article */ +.article-wrap { max-width: 700px; margin: 0 auto; padding: 8.5rem 2rem 5rem; } +.back-link { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; font-weight: 600; color: var(--cookie); text-decoration: none; margin-bottom: 2.2rem; transition: gap 0.2s; } +.back-link:hover { gap: 0.7rem; } +.article-wrap .post-meta { margin-bottom: 1.1rem; } +.article-wrap h1 { font-family: 'Fraunces', serif; font-size: clamp(2.1rem, 5vw, 3.1rem); font-weight: 900; letter-spacing: -0.03em; color: var(--chip); line-height: 1.1; margin-bottom: 1.2rem; } +.lede { font-size: 1.12rem; font-weight: 300; color: var(--text-muted); line-height: 1.7; margin-bottom: 2.5rem; } +article h2 { font-family: 'Fraunces', serif; font-size: 1.5rem; font-weight: 700; color: var(--chip); letter-spacing: -0.02em; margin: 2.4rem 0 0.9rem; } +article p { font-size: 1rem; line-height: 1.8; color: var(--text-main); margin-bottom: 1.2rem; } +article a { color: var(--cookie); font-weight: 500; } +article code { font-family: 'DM Mono', monospace; font-size: 0.85em; background: rgba(26,107,92,0.08); color: var(--byte); padding: 0.1em 0.4em; border-radius: 0.4em; } +article.cat-crew code { background: rgba(107,63,160,0.08); color: var(--rocky); } + +.callout { border-radius: 0.9rem; padding: 1.1rem 1.4rem; font-size: 0.92rem; line-height: 1.7; margin: 1.5rem 0; background: #FDF0E0; border-left: 4px solid var(--cookie); color: var(--cookie-dark); } +.cat-tech .callout { background: var(--byte-light); border-left-color: var(--byte); color: var(--byte); } +.cat-crew .callout { background: #F3E8FB; border-left-color: var(--rocky); color: var(--rocky); } + +.divider { text-align: center; font-size: 1.1rem; letter-spacing: 0.8rem; color: var(--cookie); margin: 2.2rem 0; } + +.author-box { display: flex; align-items: center; gap: 1rem; background: var(--soft-white); border: 1px solid var(--border); border-radius: 1.5rem; padding: 1.4rem 1.6rem; margin-top: 3rem; } +.author-avatar { width: 48px; height: 48px; border-radius: 50%; background: var(--chip); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; flex-shrink: 0; } +.author-avatar.avatar-tech { background: var(--byte); } +.author-avatar.avatar-crew { background: var(--rocky); } +.author-box strong { font-family: 'Fraunces', serif; font-size: 1rem; color: var(--chip); display: block; margin-bottom: 0.15rem; } +.author-box span { font-size: 0.82rem; color: var(--text-muted); line-height: 1.5; } diff --git a/assets/css/home.css b/assets/css/home.css new file mode 100644 index 0000000..0354ecc --- /dev/null +++ b/assets/css/home.css @@ -0,0 +1,106 @@ +/* CookieBytes — landing page styles */ +.hero { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 8rem 2rem 4rem; position: relative; overflow: hidden; } +.hero-bg { position: absolute; inset: 0; z-index: 0; background: radial-gradient(ellipse 80% 60% at 50% 110%, rgba(201,123,58,0.12) 0%, transparent 70%), radial-gradient(circle at 15% 20%, rgba(240,201,138,0.18) 0%, transparent 40%), radial-gradient(circle at 85% 15%, rgba(38,168,141,0.08) 0%, transparent 35%); } +.floating-cookies { position: absolute; inset: 0; z-index: 0; pointer-events: none; } +.fc { position: absolute; opacity: 0.12; animation: floatUp linear infinite; } +@keyframes floatUp { 0% { transform: translateY(0) rotate(0deg); opacity: 0; } 10% { opacity: 0.12; } 90% { opacity: 0.12; } 100% { transform: translateY(-100px) rotate(20deg); opacity: 0; } } +.hero h1 { font-family: 'Fraunces', serif; font-size: clamp(3.2rem, 8vw, 6.5rem); font-weight: 900; line-height: 1.0; letter-spacing: -0.03em; color: var(--chip); max-width: 820px; position: relative; z-index: 1; margin-bottom: 1.6rem; animation: fadeSlideIn 0.7s 0.1s ease both; } +.hero h1 em { font-style: normal; color: var(--cookie); position: relative; } +.hero h1 em::after { content: ''; position: absolute; left: 0; right: 0; bottom: -4px; height: 3px; background: var(--cookie-light); border-radius: 2px; } +.hero-sub { font-size: 1.15rem; font-weight: 300; line-height: 1.65; color: var(--text-muted); max-width: 520px; position: relative; z-index: 1; margin-bottom: 2.8rem; animation: fadeSlideIn 0.7s 0.2s ease both; } +.hero-actions { display: flex; gap: 1rem; flex-wrap: wrap; justify-content: center; position: relative; z-index: 1; animation: fadeSlideIn 0.7s 0.3s ease both; } +.btn-primary { background: var(--cookie); color: white; padding: 0.85rem 2.2rem; border-radius: 3rem; font-size: 0.95rem; font-weight: 600; text-decoration: none; border: none; cursor: pointer; transition: background 0.2s, transform 0.15s, box-shadow 0.2s; box-shadow: 0 4px 18px rgba(201,123,58,0.35); } +.btn-primary:hover { background: var(--cookie-dark); transform: translateY(-2px); } +.btn-secondary { background: transparent; color: var(--chip); padding: 0.85rem 2.2rem; border-radius: 3rem; font-size: 0.95rem; font-weight: 500; text-decoration: none; border: 1.5px solid var(--border); cursor: pointer; transition: border-color 0.2s, color 0.2s, transform 0.15s; } +.btn-secondary:hover { border-color: var(--cookie); color: var(--cookie); transform: translateY(-2px); } +.dog-strip { position: relative; z-index: 1; margin-top: 4.5rem; display: flex; align-items: center; gap: 1rem; background: var(--soft-white); border: 1px solid var(--border); border-radius: 2rem; padding: 0.75rem 1.5rem; font-size: 0.85rem; color: var(--text-muted); animation: fadeSlideIn 0.7s 0.4s ease both; } +.dog-avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--chip); display: flex; align-items: center; justify-content: center; } + +section { padding: 6rem 2rem; } +.container { max-width: 1100px; margin: 0 auto; } +.section-label { font-family: 'DM Mono', monospace; font-size: 0.75rem; font-weight: 500; color: var(--cookie); letter-spacing: 0.15em; text-transform: uppercase; margin-bottom: 0.9rem; } +.section-title { font-family: 'Fraunces', serif; font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; color: var(--chip); line-height: 1.15; letter-spacing: -0.02em; max-width: 600px; margin-bottom: 1.2rem; } +.section-desc { font-size: 1rem; color: var(--text-muted); line-height: 1.7; max-width: 500px; margin-bottom: 3rem; } + +.services-bg { background: var(--soft-white); } +.services-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 3.5rem; } +.service-card { background: white; border: 1px solid var(--border); border-radius: 1.5rem; padding: 2rem 1.75rem; transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; cursor: default; } +.service-card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(201,123,58,0.12); border-color: var(--cookie-light); } +.service-icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; margin-bottom: 1.2rem; } +.service-card h3 { font-family: 'Fraunces', serif; font-weight: 700; font-size: 1.2rem; color: var(--chip); margin-bottom: 0.6rem; } +.service-card p { font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; } + +.about-intro { max-width: 640px; margin-bottom: 3.5rem; } +.crew-divider { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.2rem; } +.crew-divider span { font-family: 'DM Mono', monospace; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap; color: var(--text-muted); } +.crew-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); } +.crew-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; } +@media (max-width: 900px) { .crew-grid { grid-template-columns: repeat(2, 1fr); } } +@media (max-width: 720px) { .crew-grid { grid-template-columns: repeat(2, 1fr); } } +.crew-card { border-radius: 1.75rem; padding: 1.8rem 1.5rem; text-align: center; position: relative; overflow: hidden; } +.crew-card::before { content: ''; position: absolute; top: -40px; right: -40px; width: 130px; height: 130px; border-radius: 50%; background: rgba(255,255,255,0.06); } +.crew-card.cookie-card { background: var(--chip); } +.crew-card.billy-card { background: var(--byte); } +.crew-card.rocky-card { background: var(--rocky); } +.crew-card.peggy-card { background: var(--peggy); } +.crew-emoji { font-size: 3.2rem; display: block; margin-bottom: 0.9rem; position: relative; z-index: 1; } +.cookie-card .crew-emoji { animation: wagTail 3s ease infinite; } +.billy-card .crew-emoji { animation: billyBounce 1.2s ease infinite; } +.rocky-card .crew-emoji { animation: rockyPounce 1s ease infinite; } +.peggy-card .crew-emoji { animation: peggyBlink 5s ease infinite; } +@keyframes wagTail { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(3deg); } } +@keyframes billyBounce { 0%, 100% { transform: translateY(0) rotate(-5deg); } 50% { transform: translateY(-8px) rotate(5deg); } } +@keyframes rockyPounce { 0%, 100% { transform: translateY(0) rotate(8deg) scale(1); } 50% { transform: translateY(-10px) rotate(-8deg) scale(1.1); } } +@keyframes peggyBlink { 0%, 90%, 100% { transform: scale(1); } 95% { transform: scaleY(0.85); } } +.crew-card h3 { font-family: 'Fraunces', serif; font-weight: 900; font-size: 1.35rem; color: var(--cream); position: relative; z-index: 1; margin-bottom: 0.4rem; } +.crew-card p { font-size: 0.82rem; color: rgba(253,246,236,0.58); position: relative; z-index: 1; line-height: 1.55; } +.crew-chip { display: inline-block; margin-top: 0.9rem; color: white; font-family: 'DM Mono', monospace; font-size: 0.67rem; padding: 0.25rem 0.8rem; border-radius: 1rem; position: relative; z-index: 1; letter-spacing: 0.06em; background: rgba(255,255,255,0.18); } +.cookie-card .crew-chip { background: var(--cookie); } +.stat-solo { margin-top: 3rem; text-align: center; } +.stat-num { font-family: 'Fraunces', serif; font-size: 3rem; font-weight: 900; color: var(--cookie); line-height: 1; } +.stat-label { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.4rem; } + +.cta-section { padding: 6rem 2rem; } +.cta-box { background: var(--cookie); border-radius: 2.5rem; padding: 4rem 3.5rem; position: relative; overflow: hidden; } +.cta-box::before { content: '🍪'; position: absolute; font-size: 16rem; opacity: 0.05; top: -3rem; right: -3rem; line-height: 1; pointer-events: none; } +.cta-top { text-align: center; margin-bottom: 2.8rem; } +.cta-top h2 { font-family: 'Fraunces', serif; font-weight: 900; font-size: clamp(2rem, 4vw, 3.2rem); color: white; margin-bottom: 0.7rem; letter-spacing: -0.02em; } +.cta-top p { color: rgba(255,255,255,0.75); font-size: 1rem; } +.cta-form { background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.2); border-radius: 1.75rem; padding: 2rem 2rem 1.75rem; max-width: 640px; margin: 0 auto; backdrop-filter: blur(4px); } +.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; } +@media (max-width: 560px) { .form-row { grid-template-columns: 1fr; } .cta-box { padding: 2.5rem 1.5rem; } } +.form-group { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1rem; } +.form-group label { font-size: 0.78rem; font-weight: 600; color: rgba(255,255,255,0.85); letter-spacing: 0.04em; } +.form-group input, .form-group textarea { font-family: 'Outfit', sans-serif; font-size: 0.9rem; color: var(--chip); background: rgba(255,255,255,0.92); border: 1.5px solid transparent; border-radius: 0.85rem; padding: 0.75rem 1rem; outline: none; transition: border-color 0.2s, box-shadow 0.2s; resize: none; width: 100%; } +.form-group input::placeholder, .form-group textarea::placeholder { color: var(--text-muted); } +.form-group input:focus, .form-group textarea:focus { border-color: var(--cookie-dark); box-shadow: 0 0 0 3px rgba(255,255,255,0.2); } +.form-group textarea { min-height: 100px; } +.captcha-box { background: rgba(255,255,255,0.92); border: 1.5px solid transparent; border-radius: 0.85rem; padding: 0.85rem 1rem; display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.2rem; cursor: pointer; transition: border-color 0.2s; user-select: none; } +.captcha-box:hover { border-color: rgba(255,255,255,0.5); } +.captcha-left { display: flex; align-items: center; gap: 0.75rem; } +.captcha-checkbox { width: 22px; height: 22px; border-radius: 4px; border: 2px solid #ccc; background: white; display: flex; align-items: center; justify-content: center; transition: background 0.2s, border-color 0.2s; flex-shrink: 0; } +.captcha-checkbox.checked { background: var(--byte); border-color: var(--byte); } +.captcha-checkbox svg { display: none; } +.captcha-checkbox.checked svg { display: block; } +.captcha-label { font-size: 0.85rem; color: var(--text-muted); font-weight: 500; } +.captcha-right { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; } +.captcha-logo { font-size: 1.3rem; line-height: 1; } +.captcha-brand { font-family: 'DM Mono', monospace; font-size: 0.52rem; color: var(--text-muted); letter-spacing: 0.05em; } +.form-submit { width: 100%; background: var(--chip); color: white; border: none; border-radius: 3rem; padding: 0.9rem; font-family: 'Outfit', sans-serif; font-size: 0.95rem; font-weight: 600; cursor: pointer; transition: background 0.2s, transform 0.15s; } +.form-submit:hover:not(:disabled) { background: #1a0e05; transform: translateY(-1px); } +.form-submit:disabled { opacity: 0.45; cursor: not-allowed; transform: none; } +.form-error { display: none; margin-top: 0.9rem; padding: 0.7rem 1rem; background: rgba(255,255,255,0.15); border: 1px solid rgba(255,200,200,0.5); border-radius: 0.7rem; font-size: 0.82rem; color: #ffd9d9; text-align: center; } +.form-error.visible { display: block; } +.form-success { display: none; text-align: center; padding: 1.5rem 1rem; } +.form-success.visible { display: block; } +.form-success-emoji { font-size: 3rem; display: block; margin-bottom: 0.8rem; animation: wagTail 2s ease infinite; } +.form-success h3 { font-family: 'Fraunces', serif; font-weight: 900; font-size: 1.4rem; color: white; margin-bottom: 0.5rem; } +.form-success p { font-size: 0.9rem; color: rgba(255,255,255,0.7); } +.cta-divider { display: flex; align-items: center; gap: 1rem; margin: 2rem auto 1.2rem; max-width: 640px; } +.cta-divider::before, .cta-divider::after { content: ''; flex: 1; height: 1px; background: rgba(255,255,255,0.25); } +.cta-divider span { font-size: 0.82rem; color: rgba(255,255,255,0.6); white-space: nowrap; } +.cta-email-wrap { display: flex; justify-content: center; } +.cta-email-pill { display: inline-flex; align-items: center; gap: 0.6rem; background: white; border-radius: 3rem; padding: 0.75rem 1.6rem; font-size: 0.95rem; font-weight: 700; color: var(--cookie-dark); text-decoration: none; box-shadow: 0 4px 20px rgba(0,0,0,0.12); transition: transform 0.15s, box-shadow 0.2s; } +.cta-email-pill:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(0,0,0,0.18); } +.cta-email-pill span { font-size: 1.1rem; } +.honeypot { position: absolute; left: -10000px; opacity: 0; } diff --git a/assets/css/shared.css b/assets/css/shared.css new file mode 100644 index 0000000..7089e3e --- /dev/null +++ b/assets/css/shared.css @@ -0,0 +1,30 @@ +/* CookieBytes — shared design tokens, nav, footer */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } +:root { + --cream: #FDF6EC; --cookie: #C97B3A; --cookie-dark: #8B4F1A; --cookie-light: #F0C98A; + --chip: #2D1E0F; --byte: #1A6B5C; --byte-light: #D4EDEA; --byte-bright: #26A88D; + --soft-white: #FFF9F2; --text-main: #1C130A; --text-muted: #7A5C3E; --border: rgba(201,123,58,0.18); + --rocky: #6B3FA0; --peggy: #B5477A; +} +html { scroll-behavior: smooth; } +body { font-family: 'Outfit', sans-serif; background: var(--cream); color: var(--text-main); overflow-x: hidden; } + +nav { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; padding: 1.1rem 3rem; background: rgba(253,246,236,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); } +.nav-logo { display: flex; align-items: center; gap: 0.6rem; font-family: 'Fraunces', serif; font-weight: 900; font-size: 1.45rem; color: var(--chip); text-decoration: none; letter-spacing: -0.02em; } +.nav-links { display: flex; align-items: center; gap: 2.2rem; list-style: none; } +.nav-links a { font-size: 0.88rem; font-weight: 500; color: var(--text-muted); text-decoration: none; letter-spacing: 0.03em; transition: color 0.2s; } +.nav-links a:hover { color: var(--cookie); } +.nav-links a.active { color: var(--cookie); font-weight: 600; } +.nav-cta { background: var(--chip) !important; color: var(--cream) !important; padding: 0.5rem 1.3rem !important; border-radius: 2rem !important; font-weight: 600 !important; transition: background 0.2s, transform 0.15s !important; } +.nav-cta:hover { background: var(--cookie-dark) !important; transform: translateY(-1px); } +@media (max-width: 720px) { nav { padding: 1rem 1.5rem; } .nav-links { display: none; } } + +footer { background: var(--chip); padding: 3rem 2rem 2rem; text-align: center; } +.footer-logo { display: flex; align-items: center; justify-content: center; gap: 0.6rem; font-family: 'Fraunces', serif; font-weight: 900; font-size: 1.6rem; color: var(--cream); letter-spacing: -0.02em; margin-bottom: 0.5rem; } +.footer-tagline { font-size: 0.82rem; color: rgba(253,246,236,0.4); font-family: 'DM Mono', monospace; letter-spacing: 0.05em; margin-bottom: 1.5rem; } +.footer-links { display: flex; justify-content: center; gap: 2rem; flex-wrap: wrap; list-style: none; margin-bottom: 2rem; } +.footer-links a { font-size: 0.85rem; color: rgba(253,246,236,0.45); text-decoration: none; transition: color 0.2s; } +.footer-links a:hover { color: var(--cookie-light); } +.footer-copy { font-size: 0.78rem; color: rgba(253,246,236,0.2); } + +@keyframes fadeSlideIn { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: translateY(0); } } diff --git a/blog/index.html b/blog/index.html new file mode 100644 index 0000000..3ff676a --- /dev/null +++ b/blog/index.html @@ -0,0 +1,39 @@ +--- +layout: default +title: The Cookie Jar +description: Notes from the oven — stories, tech deep-dives, and crew chaos from the CookieBytes studio. +extra_css: + - /assets/css/blog.css +--- +
+
+

// the cookie jar

+

Fresh from the oven.

+

Notes on software, smart homes, and studio life — written by humans, supervised by four animals.

+
+ +
+{% for post in site.posts %} + + {{ post.emoji }} + +

{{ post.title }}

+

{{ post.description }}

+ Read post → +
+{% endfor %} +
+ +
+ +
diff --git a/index.html b/index.html index dfe8f23..48970ab 100644 --- a/index.html +++ b/index.html @@ -1,177 +1,9 @@ - - - - - -CookieBytes - - - - - - - - +--- +layout: default +description: Software that's baked to perfection. CookieBytes builds digital products with warmth, precision, and a little love. +extra_css: + - /assets/css/home.css +---
@@ -187,7 +19,7 @@

Software that's baked
to perfection.

Meet the team →
-
+
{% include cookie-logo.html size=24 id="strip" %}
Powered by code, caffeine, and 4 animals who run the place
@@ -198,13 +30,13 @@

Software that's baked
to perfection.

Full-stack flavours for every appetite.

From crispy MVPs to layered enterprise platforms — we craft software that feels exactly right.

+
📱

Mobile Apps

Native-quality mobile experiences for iOS and Android. Smooth interactions, offline-first, and ready for scale.

+
🔧

API & Backend

Robust, well-documented APIs and backend systems. Built for reliability, security, and the kind of uptime that lets you sleep at night.

+
🎨

Product Design

UX and UI with personality. We design interfaces that feel human — intuitive, warm, and worth coming back to.

🚀

DevOps & Scale

CI/CD, containerization, and cloud architecture. We get your code shipped cleanly and keep it running at any load.

🏗️

Infrastructure

From cloud provisioning to on-premise setups, we design and manage infrastructure that's resilient, cost-efficient, and built to grow with you.

🌐

Network

Secure, high-performance network design and management. We make sure your systems talk to each other reliably — and that the wrong ones don't.

🏠

Home Automation

Smart-home installs, IoT integrations, and Home Assistant setups. We make your space respond to you — lights, sensors, routines, all in sync.

-
📱

Mobile Apps

Native-quality mobile experiences for iOS and Android. Smooth interactions, offline-first, and ready for scale.

-
🔧

API & Backend

Robust, well-documented APIs and backend systems. Built for reliability, security, and the kind of uptime that lets you sleep at night.

-
🎨

Product Design

UX and UI with personality. We design interfaces that feel human — intuitive, warm, and worth coming back to.

@@ -241,7 +73,6 @@

Ready to bake something great?

- @@ -278,42 +109,10 @@

Message received!

-
- - - -