This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a multilingual Jekyll website for Mirigi, a digital concierge service for condominiums. The site showcases features and customer testimonials in English, Spanish, and French. It’s built using Jekyll with a Bootstrap-based theme and Docker deployment.
When fact-checking or extending any feature/FAQ/copy, ground claims against the actual implementation. The three sibling repos cover the three product surfaces:
| Repo | Path (local sibling) | Tech | Use it to verify… |
|---|---|---|---|
| Backend | /home/fede/repos/mirigi/mirigi/mirigi-backend/ |
Python (app/) |
Source of truth. Endpoints, business logic, state machines, AI tools (mirigi_* in app/resident/ws/v2/ai_chat.py), event constants, schema, all real capabilities. Search app/staff/, app/resident/, app/model/ for grounding. |
| Resident frontend | /home/fede/repos/mirigi/mirigi-frontend/ |
React | What residents see/do. Use to verify the resident-side UI for any feature claim. |
| Staff portal | /home/fede/repos/mirigi/mirigi-staff/ |
React 19 + Vite + TypeScript + Tailwind v4, runs on :3001, see src/pages/ and src/api/ |
What staff and management see/do. Use to verify staff-console claims (operations dashboards, reports, valet queue, package log, role-based access). |
Grounding rule: if a claim names a specific capability — “residents can ask Miri to book the amenity”, “the staff console flags packages for pickup”, “boards run custom reports on demand” — open the matching repo and verify the endpoint/page/tool exists before publishing. If it doesn’t, soften the claim or remove it. The fact-check agents dispatched from this repo have explicit access to all three paths above.
Never mention technical internals on feature pages, customer pages, brochures, or proposals. The audience is luxury condominium boards, property managers and residents — not developers. Describe the experience (what the user sees and gets), not the mechanism (how the code does it).
When summarizing or fact-checking content, replace technical names with the user-visible behavior they enable:
| Internal / technical term | User-facing replacement |
|---|---|
| MQTT, WebSocket, long-polling | “real-time updates” / “live notifications” |
| Twilio, SMS provider names | “SMS” |
| Cordova, Capacitor, React Native (framework) | “companion app” / “branded mobile app” |
| MJPEG, RTSP, ONVIF, HLS, HTTP video endpoint | “video streams” |
| OAuth, OAuth 2.0, OAuth pairing, SSO protocol names | “pairs securely” / “secure API” |
| RSA, PKCS, JWT, signed JWT | “cryptographically signed” / “secure” |
| S3, blob storage, CDN | “secure storage” |
| MIME, multipart, base64 | omit, or “file upload” |
| HTTP, REST, JSON, GraphQL | “secure API” / “integration” |
| SQL query, database query, raw query | “fully customizable” / “tailored to the property” / “defined per building” — do not name the authoring mechanism |
| Prosis, administracion.com.uy (vendor) | “the administrator’s accounting platform” |
| BLI, BeoLiving Intelligence (technical name) | “smart-home controller” (when audience won’t recognize BLI). BLI/Khimo OK on the home automation page itself — they are brand-marketed by Bang & Olufsen and recognized by integrators. |
| Axis (camera brand), specific camera models | “IP cameras” |
Always-forbidden (regardless of context — off-brand or false):
Always-OK technical terms (widely understood, marketing-friendly):
When in doubt: “Residents are notified the moment their car is ready” beats “the system pushes an MQTT event on state change”.
# Build and serve using Docker (recommended)
docker build -t mirigi-jekyll-site .
docker run -v $(pwd):/usr/src/app -p 4000:4000 mirigi-jekyll-site
# Or use the Makefile (requires docker image to be built first)
make serve
# For frontend asset development (SCSS/JS changes)
npm install
npm run start # Runs gulp watch with BrowserSync on port 3000
make all # Build frontend assets using gulp (runs npm install if needed)
gulp build # Build CSS/JS assets and copy vendor dependencies
gulp css # Compile SCSS only
gulp vendor # Copy node_modules dependencies to /vendor/
_features) and Customers (_customers)/en/features/, /en/customers//es/funcionalidades/, /es/clientes//fr/caracteristiques/, /fr/clients/collections/_features/[lang]/ - Feature descriptions (en, es, fr subdirectories)collections/_customers/[lang]/ - Customer testimonials (en, es, fr subdirectories)_layouts/ - Jekyll layout templates (feature.html, customer.html, index.html)_includes/ - Reusable components (header.html, footer.html, feature_div.html)_data/[lang].yml - UI labels and translations, accessed via site.data[page.lang]scss/ - SASS source files (entry point: grayscale.scss; brochure styles: _brochure.scss)img/ and img_mirigi/ - Static imagesdefault.html — base with navigation/footerdefaultcontent.html — extends default with content-specific stylingfeature.html, customer.html — collection item pagesindex.html, index_slide.html — homepage variantsbrochure.html — static marketing brochure (print-optimized, standalone)proposal-builder.html — interactive proposal form (standalone, no nav)proposal.html — printable proposal output (standalone, no nav)post.html — blog layoutFeatures and customers are Jekyll collections. Each content piece requires this frontmatter:
---
title: "Feature Name"
image: "/img_mirigi/feature-image.jpg"
layout: feature # or "customer"
description: "Short description for SEO"
keywords: keyword1, keyword2, keyword3
image_credits: '@<a href="url">author</a>' # optional
---
The lang and permalink are auto-set by _config.yml defaults based on file path.
scss/ to css/ (both expanded and minified versions)node_modules/ to vendor/ directory (not linked — enables offline serving)gulp watchWhen adding content, create the same file (same filename) in all three language directories:
collections/_features/en/my-feature.mdcollections/_features/es/my-feature.mdcollections/_features/fr/my-feature.mdbrochure.html is a static, print-optimized marketing brochure (US Letter, 8.5”×11”). Styled by scss/_brochure.scss with luxury typography (Playfair Display, Montserrat) and a dark charcoal/pearl/gold palette.
Key SCSS variables:
$print-page-height: 11in
$print-page-width: 8.5in
$print-margin: 0.5in
$brochure-dark-bg: #58595b
$brochure-light-bg: #f5f5f5
Key CSS classes:
.brochure-page — page wrapper with min-height 10in.brochure-feature — flex row, 40% image / 60% text; .reverse flips layout.brochure-customers — 2-column grid@media print — enforces exact dimensions, forces color (-webkit-print-color-adjust: exact), hides UI buttonsThe same CSS framework and components (_includes/brochure_feature.html, _includes/brochure_customer.html) are reused in proposal.html for dynamic proposals.
The site includes a sales proposal generator system that allows salespeople to create customized PDF proposals for buildings.
js/proposal-crypto.js — HMAC-SHA256 authentication + AES-GCM encryption/decryption; renders login modal with blur-locked UIjs/proposal-storage.js — IndexedDB persistence; save/load/delete proposals; generates encrypted share URLsjs/proposal-utils.js — ProposalURLBuilder/ProposalURLParser classes; HTML escaping; feature slug lookup; clipboard operationsDecrypts ?d= URL parameter and dynamically renders:
The proposal system uses a key-based authentication with encrypted URL parameters:
Authentication Flow:
1. User enters symmetric key (only the user knows it - NOT stored in code)
2. localStoredKey = HMAC-SHA256(symmetricKey, SALT)
3. accessHash = HMAC-SHA256(localStoredKey, SALT)
4. Verify: accessHash === ACCESS_HMAC (pre-calculated constant in code)
5. encryptionKey = HMAC-SHA256(localStoredKey, "encrypt") - for AES encryption
SALT = "mirigi is the best smartbuilding solution"
ACCESS_HMAC = "c5a1de2293c3aed54d86479d5e8df92a685a97aadcde11563491a7fc8d17af5c"
Security properties:
proposal.html?d=<encrypted_base64_data> - opaque to usersKey derivation:
// Login verification (symmetric key only exists in user's memory)
localStoredKey = HMAC(userEnteredKey, SALT)
accessHash = HMAC(localStoredKey, SALT)
isValid = (accessHash === ACCESS_HMAC) // ACCESS_HMAC is pre-calculated constant
// Encryption key derivation
encryptionKey = HMAC(localStoredKey, "encrypt") // Used for AES-GCM
To change the access key: Calculate new ACCESS_HMAC offline:
node -e "
const crypto = require('crypto');
const SALT = 'mirigi is the best smartbuilding solution';
const newKey = 'YOUR_NEW_KEY';
const localKey = crypto.createHmac('sha256', SALT).update(newKey).digest('hex');
const ACCESS_HMAC = crypto.createHmac('sha256', SALT).update(localKey).digest('hex');
console.log('ACCESS_HMAC:', ACCESS_HMAC);
"
All three language versions use the shared layout with just frontmatter:
proposal-builder.html (en)es/proposal-builder.html (es)fr/proposal-builder.html (fr)Login UI strings are in _data/[lang].yml:
login_title, login_subtitle, login_placeholderlogin_button, login_error, logout