Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .atl/.skill-registry.cache.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"fingerprint": "8ce0f782458fd84c13e3883951680bab77a6e636"
"fingerprint": "0678725588c991a9d666785ac891001531c04464"
}
19 changes: 18 additions & 1 deletion .atl/skill-registry.md

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions PROJECT_HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Rentalled — Project Handoff

## Current Status

The Rental Led landing page is implemented locally and the SDD cycle was completed and archived. After the first GitHub push, the visual design was criticized and then revised locally. The latest local version includes the corrected visual polish and the screenshot fixes, but those latest visual changes have **not** been committed or pushed yet.

## Repository

- Local path: `/Users/fernandogabrielrusso/Desktop/Rentalled`
- GitHub repo: `https://github.com/russofg/Rentalled`
- Visibility: private
- Initial pushed commit: `089d499 feat: add Rental Led landing`
- Current local state: visual redesign/fixes after that commit are local only until committed and pushed.

## Production Values

- Domain: `https://rentalled.com.ar`
- WhatsApp: `+54 9 11 4973 4510`
- WhatsApp URL: `https://wa.me/5491149734510`
- Instagram: `https://www.instagram.com/rentalled.ar`
- Instagram handle: `@rentalled.ar`

## Latest Visual Feedback and Fixes

The user rejected the first visual direction as not professional enough. Specific issues included generic/repetitive copy, stretched-looking typography, ugly service cards, and weak FAQ styling.

Then a second screenshot showed two concrete issues:

1. The skip link `Saltar al contenido principal` was visible by default.
2. The Clients section had broken narrow vertical cards with clipped text.

Latest local fixes:

- Skip link now uses an accessible visually-hidden pattern and only appears on keyboard focus.
- Clients section was redesigned into responsive audience cards with safe widths and no clipped headings.
- The broader visual direction was revised toward a premium industrial/editorial event-production style.
- Typography, hero, service presentation, FAQ, copy density, and repeated credibility copy were improved.

## Verification Commands

The latest local fixes passed:

```bash
npm run build
npm run check:seo
npm run check:final
```

Latest reported final check:

- Client JS: `565 bytes` gzipped
- SEO production values valid
- PNG OG image valid
- Static accessibility/motion checks pass

## Important Files

- `src/content/site.ts` — centralized bilingual content, production contact values, metadata source.
- `src/styles/global.css` — typography, layout, responsive behavior, skip link, cards, motion, tap targets.
- `src/components/Clients.astro` — latest screenshot-driven redesign.
- `src/components/Hero.astro` — revised visual direction with image-backed hero.
- `src/components/Services.astro` — revised service presentation.
- `src/components/Faq.astro` — revised FAQ presentation.
- `src/layouts/BaseLayout.astro` — shared layout, SEO/schema injection, skip link markup.
- `scripts/check-seo.mjs` — validates SEO/schema/sitemap/robots/production values.
- `scripts/check-final.mjs` — validates final static/accessibility/motion/JS budget checks.
- `public/og-rental-led.png` — 1200x630 PNG generated from `Logo.png` for OG/Twitter.
- `openspec/changes/archive/2026-07-05-add-rental-led-landing/` — archived SDD audit trail.
- `openspec/specs/rental-led-landing/spec.md` — source-of-truth spec after archive.

## Next Session Checklist

1. Open `localhost:4321` and visually review the latest local version.
2. If approved, run:

```bash
npm run build && npm run check:seo && npm run check:final
git status --short
git diff
```

3. Commit the local visual fixes.
4. Push to `origin/main`.
5. Configure deployment/DNS for `rentalled.com.ar` when ready.

## Remaining Non-Code Follow-up

- Add production monitoring/rollback notes if this site will be maintained actively after launch.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"build": "astro build",
"check:seo": "node scripts/check-seo.mjs",
"check:final": "node scripts/check-final.mjs",
"verify": "npm run build && npm run check:seo && npm run check:final",
"preview": "astro preview",
"astro": "astro"
},
Expand Down
Binary file added public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/fonts/ClashDisplay-Bold.woff2
Binary file not shown.
Binary file added public/fonts/ClashDisplay-Semibold.woff2
Binary file not shown.
Binary file added public/fonts/JetBrainsMono-Medium.woff2
Binary file not shown.
Binary file added public/fonts/JetBrainsMono-Regular.woff2
Binary file not shown.
Binary file added public/fonts/Satoshi-Black.woff2
Binary file not shown.
Binary file added public/fonts/Satoshi-Bold.woff2
Binary file not shown.
Binary file added public/fonts/Satoshi-Medium.woff2
Binary file not shown.
Binary file added public/fonts/Satoshi-Regular.woff2
Binary file not shown.
27 changes: 27 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Rental Led

> Rental Led rents and operates LED screens for corporate and social events in CABA and Buenos Aires, Argentina.

Rental Led provides LED screen rental, setup, live operation, and technical support for events. The service is aimed at companies, agencies, production teams, venues, and social event organizers. The site is available in Spanish and English.

## Core services
- LED screens for corporate events, launches, conferences, congresses, and brand activations.
- LED screens for social events, shows, celebrations, and private experiences.
- Technical planning, setup, live operation, and event-day support with an in-house crew.
- Coordination with venues, agencies, producers, sound, lighting, and broader event production teams.

## Technical details
- Indoor LED screens: 2.6 mm pixel pitch.
- Outdoor LED screens: 3.9 mm pixel pitch.

## Service area
- CABA (Ciudad Autónoma de Buenos Aires) and Buenos Aires Province, Argentina.

## Contact and booking
- WhatsApp: +54 9 11 4973 4510 — https://wa.me/5491149734510
- Instagram: @rentalled.ar — https://www.instagram.com/rentalled.ar

## Canonical pages
- Spanish homepage: https://rentalled.com.ar/
- English homepage: https://rentalled.com.ar/en/
- Sitemap: https://rentalled.com.ar/sitemap-index.xml
Binary file added public/logo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084628-1.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084631.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084639.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084645.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084648.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084679.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000084684.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000085410.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000085416.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000085426.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000118364.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000118367.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000118370.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000140534-about.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/portfolio/1000140534.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions scripts/check-seo.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const expectedPages = [
},
{
question: "¿Cómo se solicita una cotización?",
answer: "Puede escribir por WhatsApp con fecha, lugar, horario y tipo de evento para recibir una propuesta."
answer: "Podés escribir por WhatsApp con fecha, lugar, horario y tipo de evento para recibir una propuesta."
},
{
question: "¿Con cuánto tiempo conviene consultar?",
Expand Down Expand Up @@ -271,7 +271,7 @@ const validateJsonLd = (page, html) => {
const service = findSchema(schemas, "Service");

check(organization?.url === urls.es, `${label} Organization URL must be ${urls.es}.`);
check(organization?.logo === expectedLaunch.ogImageUrl, `${label} Organization logo must use the PNG OG image.`);
check(organization?.logo === `${baseUrl}/logo.webp`, `${label} Organization logo must use the production logo.`);
check(organization?.image === expectedLaunch.ogImageUrl, `${label} Organization image must use the PNG OG image.`);
check(Array.isArray(organization?.sameAs) && organization.sameAs.includes(expectedLaunch.instagramUrl), `${label} Organization sameAs must include ${expectedLaunch.instagramUrl}.`);

Expand Down
46 changes: 35 additions & 11 deletions src/components/About.astro
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
---
import type { LocalizedPage } from "@content/site";
import Icon from "./Icon.astro";

export interface Props {
page: LocalizedPage;
}

const { page } = Astro.props;
const about = page.sections.about;
---

<section id="about" class="section-shell split-section" aria-labelledby="about-title">
<div>
<p class="eyebrow">{page.sections.about.eyebrow}</p>
<h2 id="about-title">{page.sections.about.title}</h2>
</div>
<div class="text-panel">
<p class="section-body">{page.sections.about.body}</p>
<p class="stat-card">{page.sections.about.stat}</p>
<ul class="check-list">
{page.sections.about.details.map((detail) => <li>{detail}</li>)}
</ul>
<section id="about" class="rl-about" aria-labelledby="about-title">
<div class="rl-about-grid rl-reveal">
<div class="rl-about-copy">
<p class="eyebrow rl-eyebrow-accent">{about.eyebrow}</p>
<h2 id="about-title" class="rl-about-h">{about.title}</h2>
<p class="rl-about-body">{about.body}</p>

<div class="rl-about-stat">
<span class="rl-about-num rl-grad">{about.years}</span>
<span class="rl-about-yearslabel">{about.yearsLabel}</span>
</div>

<ul class="rl-about-list">
{about.details.map((detail) => (
<li>
<span class="rl-check"><Icon name="check" size={15} /></span>
{detail}
</li>
))}
</ul>
</div>

<div class="rl-about-media">
<img
class="rl-about-img"
src={`/portfolio/${about.imageFileName}`}
alt={about.imageAlt}
width={about.imageWidth}
height={about.imageHeight}
loading="lazy"
decoding="async"
/>
</div>
</div>
</section>
22 changes: 22 additions & 0 deletions src/components/BrandIcon.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
// Brand glyphs (WhatsApp, Instagram) — inline, self-contained.
export interface Props {
name: "whatsapp" | "instagram";
size?: number;
}
const { name, size = 18 } = Astro.props;
---

{name === "whatsapp" && (
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M17.5 14.4c-.3-.15-1.77-.87-2.04-.97-.27-.1-.47-.15-.67.15-.2.3-.77.97-.95 1.17-.17.2-.35.22-.65.07-.3-.15-1.26-.46-2.4-1.48-.89-.79-1.49-1.77-1.66-2.07-.17-.3-.02-.46.13-.61.13-.13.3-.35.45-.52.15-.17.2-.3.3-.5.1-.2.05-.37-.02-.52-.07-.15-.67-1.62-.92-2.22-.24-.58-.49-.5-.67-.51h-.57c-.2 0-.52.07-.79.37-.27.3-1.04 1.02-1.04 2.48s1.06 2.88 1.21 3.08c.15.2 2.1 3.2 5.08 4.49.71.31 1.26.49 1.69.62.71.23 1.36.2 1.87.12.57-.08 1.77-.72 2.02-1.42.25-.7.25-1.29.17-1.42-.07-.13-.27-.2-.57-.35z"/>
<path d="M12 2A10 10 0 0 0 3.5 17.2L2 22l4.9-1.5A10 10 0 1 0 12 2zm0 18.2c-1.5 0-2.98-.4-4.27-1.16l-.3-.18-3.1.95.99-3.02-.2-.31A8.2 8.2 0 1 1 12 20.2z"/>
</svg>
)}
{name === "instagram" && (
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<rect x="2" y="2" width="20" height="20" rx="5.5"/>
<circle cx="12" cy="12" r="4.2"/>
<circle cx="17.4" cy="6.6" r="1.2" fill="currentColor" stroke="none"/>
</svg>
)}
38 changes: 29 additions & 9 deletions src/components/Clients.astro
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
---
import { translate, type LocalizedPage } from "@content/site";
import { site, translate, type LocalizedPage } from "@content/site";
import Icon from "./Icon.astro";
import BrandIcon from "./BrandIcon.astro";

export interface Props {
page: LocalizedPage;
}

const { page } = Astro.props;
const locale = page.locale;
const clients = page.sections.clients;
const ctaTitle = locale === "es" ? "¿Tu equipo produce eventos?" : "Does your team produce events?";
const ctaBody = locale === "es" ? "Sumá una operación LED confiable a tu próxima producción." : "Add reliable LED operation to your next production.";
const ctaLabel = locale === "es" ? "Hablemos" : "Let's talk";
---

<section id="clients" class="section-shell split-section" aria-labelledby="clients-title">
<div>
<p class="eyebrow">{page.sections.clients.eyebrow}</p>
<h2 id="clients-title">{page.sections.clients.title}</h2>
<p class="section-body">{page.sections.clients.body}</p>
<section id="clients" class="rl-section" aria-labelledby="clients-title">
<div class="rl-section-head rl-reveal">
<p class="eyebrow rl-eyebrow-accent">{clients.eyebrow}</p>
<h2 id="clients-title" class="rl-section-title">{clients.title}</h2>
<p class="rl-section-body">{clients.body}</p>
</div>

<div class="client-list">
{page.clients.map((client) => (
<article class="client-item">
<div class="rl-cards">
{page.clients.map((client, index) => (
<article class="rl-feature rl-feature-client rl-reveal" data-delay={(index % 3) + 1}>
<span class="rl-feature-ic rl-feature-ic-solid"><Icon name={client.icon} size={20} /></span>
<h3>{translate(client.title, locale)}</h3>
<p>{translate(client.description, locale)}</p>
<span class="rl-feature-arrow" aria-hidden="true"><Icon name="arrow-up-right" size={18} /></span>
</article>
))}

<a
class="rl-feature rl-feature-cta rl-reveal"
data-delay="3"
href={site.contact.whatsappUrl}
target="_blank"
rel="noopener noreferrer"
>
<h3>{ctaTitle}</h3>
<p>{ctaBody}</p>
<span class="rl-feature-ctabtn"><BrandIcon name="whatsapp" size={17} />{ctaLabel}</span>
</a>
</div>
</section>
52 changes: 34 additions & 18 deletions src/components/Contact.astro
Original file line number Diff line number Diff line change
@@ -1,33 +1,49 @@
---
import { site, type LocalizedPage } from "@content/site";
import Icon from "./Icon.astro";
import BrandIcon from "./BrandIcon.astro";

export interface Props {
page: LocalizedPage;
}

const { page } = Astro.props;
const locale = page.locale;
const contact = page.sections.contact;
---

<section id="contact" class="section-shell contact-section" aria-labelledby="contact-title">
<div>
<p class="eyebrow">{page.sections.contact.eyebrow}</p>
<h2 id="contact-title">{page.sections.contact.title}</h2>
<p class="section-body">{page.sections.contact.body}</p>
</div>
<section id="contact" class="rl-contact" aria-labelledby="contact-title">
<div class="rl-contact-card rl-reveal">
<div class="rl-contact-inner">
<p class="eyebrow rl-eyebrow-accent">{contact.eyebrow}</p>
<h2 id="contact-title" class="rl-contact-h">{contact.title}</h2>
<p class="rl-contact-body">{contact.body}</p>

<div class="rl-contact-actions">
<a class="btn btn-lg btn-primary" href={site.contact.whatsappUrl} target="_blank" rel="noopener noreferrer">
<BrandIcon name="whatsapp" size={18} />
{contact.whatsappLabel}
</a>
<a class="btn btn-lg btn-ghost" href={site.contact.instagramUrl} target="_blank" rel="noopener noreferrer">
<BrandIcon name="instagram" size={18} />
{contact.instagramLabel}
</a>
</div>

<div class="contact-panel">
<p class="contact-location">
<span>{page.sections.contact.locationLabel}</span>
{site.contact.location[locale]}
</p>
<div class="cta-row">
<a class="button button-primary" href={site.contact.whatsappUrl} target="_blank" rel="noopener noreferrer">
{page.sections.contact.whatsappLabel}
</a>
<a class="button button-secondary" href={site.contact.instagramUrl} target="_blank" rel="noopener noreferrer" translate="no">
{page.sections.contact.instagramLabel}
</a>
<dl class="rl-contact-meta">
<div>
<dt class="eyebrow">{contact.locationLabel}</dt>
<dd><Icon name="map-pin" size={16} />{site.contact.location[locale]}</dd>
</div>
<div>
<dt class="eyebrow">WhatsApp</dt>
<dd class="mono"><Icon name="phone" size={15} />{site.contact.whatsappDisplay}</dd>
</div>
<div>
<dt class="eyebrow">Instagram</dt>
<dd>{site.contact.instagramHandle}</dd>
</div>
</dl>
</div>
</div>
</section>
27 changes: 17 additions & 10 deletions src/components/Faq.astro
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
---
import { translate, type LocalizedPage } from "@content/site";
import Icon from "./Icon.astro";

export interface Props {
page: LocalizedPage;
}

const { page } = Astro.props;
const locale = page.locale;
const faq = page.sections.faq;
---

<section id="faq" class="section-shell" aria-labelledby="faq-title">
<div class="section-heading">
<p class="eyebrow">{page.sections.faq.eyebrow}</p>
<h2 id="faq-title">{page.sections.faq.title}</h2>
<p class="section-body">{page.sections.faq.body}</p>
<section id="faq" class="rl-faq" aria-labelledby="faq-title">
<div class="rl-section-head rl-reveal">
<p class="eyebrow rl-eyebrow-accent">{faq.eyebrow}</p>
<h2 id="faq-title" class="rl-section-title">{faq.title}</h2>
<p class="rl-section-body">{faq.body}</p>
</div>

<div class="faq-list">
{page.faq.map((item) => (
<details class="faq-item">
<summary>{translate(item.question, locale)}</summary>
<p>{translate(item.answer, locale)}</p>
<div class="rl-faq-list" data-faq>
{page.faq.map((item, index) => (
<details class="rl-faq-item faq-item rl-reveal" open={index === 0 ? true : undefined}>
<summary class="rl-faq-q">
<span>{translate(item.question, locale)}</span>
<span class="rl-faq-icon"><Icon name="plus" size={17} /></span>
</summary>
<div class="rl-faq-panel" id={`faq-panel-${item.id}`}>
<p>{translate(item.answer, locale)}</p>
</div>
</details>
))}
</div>
Expand Down
Loading