Auto commit: 2026-04-06T06:55:26.329Z

This commit is contained in:
Flatlogic Bot 2026-04-06 06:55:26 +00:00
parent 066b212d79
commit ffd5ebcc11
5 changed files with 465 additions and 68 deletions

View File

@ -5,7 +5,7 @@
@section('content')
<section class="split">
<article class="card success">
<article class="card success form-card">
<span class="eyebrow">6 · Reserva confirmada</span>
<h1 style="font-size:clamp(2.15rem,4vw,3.7rem);max-width:14ch;">Reserva cerrada.</h1>
<p>
@ -13,6 +13,27 @@
Aquí se ve el valor del MVP: un trayecto real terminó en una intención comercial medible y atribuible.
</p>
<div class="route-card">
<small>Resultado visible</small>
<strong>{{ $booking->offer->title }}</strong>
<span>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 2) : 'Importe pendiente' }} · {{ ucfirst($booking->status) }} · {{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</span>
</div>
<div class="screen-kpis">
<div class="screen-kpi">
<strong>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 0) : '—' }}</strong>
<span>importe</span>
</div>
<div class="screen-kpi">
<strong>{{ $booking->commission_amount ? '€'.number_format((float) $booking->commission_amount, 0) : '—' }}</strong>
<span>comisión</span>
</div>
<div class="screen-kpi">
<strong>{{ $booking->recommendation?->id ? 'Top '.$booking->recommendation->position : 'Directa' }}</strong>
<span>atribución</span>
</div>
</div>
<div class="list">
<div class="list-item">Booking UUID: {{ $booking->uuid }}</div>
<div class="list-item">Estado: {{ ucfirst($booking->status) }}</div>
@ -26,14 +47,24 @@
</div>
</article>
<aside class="card">
<aside class="card form-card">
<span class="eyebrow">Lectura de negocio</span>
<div class="list">
<div class="list-item">Ride vinculado: {{ $booking->ride?->uuid ?? 'Reserva directa' }}</div>
<div class="list-item">Canal de origen: {{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</div>
<div class="list-item">Zona: {{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</div>
<div class="list-item">Recomendación vinculada: {{ $booking->recommendation?->id ? 'Top '.$booking->recommendation->position : 'No' }}</div>
<div class="list-item">Email cliente: {{ $booking->customer_email ?: 'No informado' }}</div>
<div class="phone-screen" style="padding:18px;">
<div class="phone-topbar">
<span>Conversión atribuida</span>
<span class="phone-dot-group" aria-hidden="true">
<span class="phone-dot"></span>
<span class="phone-dot"></span>
<span class="phone-dot is-live"></span>
</span>
</div>
<div class="phone-list">
<div class="phone-list-item"><strong>Ride vinculado</strong><small>{{ $booking->ride?->uuid ?? 'Reserva directa' }}</small></div>
<div class="phone-list-item"><strong>Canal de origen</strong><small>{{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</small></div>
<div class="phone-list-item"><strong>Zona</strong><small>{{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</small></div>
<div class="phone-list-item"><strong>Recomendación vinculada</strong><small>{{ $booking->recommendation?->id ? 'Top '.$booking->recommendation->position : 'No' }}</small></div>
<div class="phone-list-item"><strong>Email cliente</strong><small>{{ $booking->customer_email ?: 'No informado' }}</small></div>
</div>
</div>
<div class="cta-row" style="margin-top:18px;">
<a class="btn" href="{{ route('home') }}">Iniciar otro trayecto</a>

View File

@ -23,23 +23,46 @@
</div>
</article>
<aside class="card orb-wrap">
<div class="stack">
<div class="mini-card">
<small>Momento exacto</small>
<h3>Durante la espera o el trayecto</h3>
<p>El usuario ya está en movimiento y receptivo. Ahí es donde la recomendación tiene sentido.</p>
<aside class="phone-shell" aria-label="Vista previa móvil de TAXILANZ">
<div class="phone-screen">
<div class="phone-topbar">
<span>{{ now()->format('H:i') }} · Lanzarote</span>
<span class="phone-dot-group" aria-hidden="true">
<span class="phone-dot"></span>
<span class="phone-dot"></span>
<span class="phone-dot is-live"></span>
</span>
</div>
<div class="mini-card">
<small>Tracking real</small>
<h3>No es humo de pitch</h3>
<p>Se registran creación del ride, vistas, clics y reserva completada para demostrar impacto.</p>
<span class="screen-badge">Hoy en Lanzarote</span>
<h2 class="screen-title">Pedir taxi</h2>
<p class="screen-copy">La app entra por movilidad y enseguida abre una siguiente decisión útil, simple y medible.</p>
<div class="route-card">
<small>Trayecto sugerido</small>
<strong>Aeropuerto César Manrique Puerto del Carmen</strong>
<span>Contexto suficiente para activar sugerencias relevantes sin parecer marketplace.</span>
</div>
<div class="mini-card success">
<small>Ángulo B2B</small>
<h3>Fácil para partners después</h3>
<p>La experiencia es simple para el turista y luego escalable para hotel, recepción o partner local.</p>
<div class="screen-kpis">
<div class="screen-kpi"><strong>{{ $metrics['rides'] }}</strong><span>rides</span></div>
<div class="screen-kpi"><strong>{{ $metrics['clicks'] }}</strong><span>clics</span></div>
<div class="screen-kpi"><strong>{{ $metrics['bookings'] }}</strong><span>bookings</span></div>
</div>
<div class="phone-list">
@foreach ($featuredOffers->take(2) as $offer)
<div class="phone-list-item">
<div class="phone-list-row">
<strong>{{ $offer->title }}</strong>
<span>{{ $loop->first ? 'Top now' : 'Next' }}</span>
</div>
<small>{{ $offer->location_label ?: 'Zona activa' }}</small>
</div>
@endforeach
</div>
<a class="btn" href="#request">Activar demo</a>
</div>
</aside>
</section>
@ -71,7 +94,7 @@
</section>
<section id="request" class="split">
<article class="card">
<article class="card form-card">
<span class="eyebrow">1 · Solicitar taxi</span>
<h2>Activa un trayecto demo</h2>
<p class="muted">Usa un origen y destino realistas de Lanzarote para que el motor pueda puntuar mejor las propuestas.</p>
@ -87,25 +110,29 @@
</div>
@endif
<div class="form-note">
Acción principal first. Cuatro campos, cero fricción y suficiente señal para contar la historia completa en móvil.
</div>
<form method="post" action="{{ route('rides.store') }}" style="margin-top:18px;">
@csrf
<div class="grid-2">
<label>
Punto de recogida
<span class="field-label">Punto de recogida</span>
<input type="text" name="pickup_label" value="{{ old('pickup_label', 'Aeropuerto César Manrique') }}" placeholder="Aeropuerto César Manrique" required>
</label>
<label>
Destino
<span class="field-label">Destino</span>
<input type="text" name="destination_label" value="{{ old('destination_label', 'Puerto del Carmen') }}" placeholder="Puerto del Carmen, Marina, Playa Blanca..." required>
</label>
</div>
<div class="grid-2">
<label>
Programado para
<span class="field-label">Programado para</span>
<input type="datetime-local" name="scheduled_for" value="{{ old('scheduled_for') }}">
</label>
<label>
Canal de origen
<span class="field-label">Canal de origen</span>
<select name="source_channel" required>
@foreach (['web' => 'Web', 'hotel' => 'Hotel', 'reception' => 'Recepción', 'app' => 'App'] as $value => $label)
<option value="{{ $value }}" @selected(old('source_channel', 'hotel') === $value)>{{ $label }}</option>
@ -131,7 +158,7 @@
</section>
<section class="section">
<div style="display:flex;justify-content:space-between;align-items:end;gap:16px;flex-wrap:wrap;margin-bottom:16px;">
<div class="section-head">
<div>
<span class="eyebrow">Inventario demo</span>
<h2>Ofertas listas para sugerir</h2>

View File

@ -282,6 +282,295 @@
min-height: 100%;
}
.phone-shell {
position: relative;
padding: 14px;
border-radius: 38px;
background: linear-gradient(160deg, rgba(15, 23, 42, 0.96), rgba(15, 118, 110, 0.92));
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 28px 70px rgba(15, 23, 42, 0.22);
max-width: 410px;
margin-inline: auto;
}
.phone-shell::before {
content: "";
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
width: 34%;
height: 28px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.92);
z-index: 2;
}
.phone-shell::after {
content: "";
position: absolute;
inset: 12px;
border-radius: 30px;
border: 1px solid rgba(255, 255, 255, 0.08);
pointer-events: none;
}
.phone-screen {
position: relative;
overflow: hidden;
border-radius: 28px;
padding: 20px 18px 18px;
min-height: 100%;
background:
radial-gradient(circle at top right, rgba(245, 158, 11, 0.20), transparent 28%),
linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(244, 247, 243, 0.96));
display: grid;
gap: 14px;
}
.phone-topbar,
.phone-list-row,
.phone-action-top,
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.phone-topbar {
font-size: 12px;
font-weight: 800;
color: var(--muted);
margin-top: 6px;
}
.phone-dot-group {
display: inline-flex;
align-items: center;
gap: 6px;
}
.phone-dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.24);
}
.phone-dot.is-live {
background: linear-gradient(135deg, var(--accent), #14b8a6);
box-shadow: 0 0 0 4px rgba(15, 118, 110, 0.12);
}
.screen-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 999px;
background: rgba(15, 118, 110, 0.10);
color: var(--accent-strong);
font-size: 12px;
font-weight: 800;
letter-spacing: 0.02em;
}
.screen-title {
margin: 0;
font-size: clamp(1.8rem, 5vw, 2.5rem);
line-height: 1;
letter-spacing: -0.05em;
}
.screen-copy {
color: var(--muted);
font-size: 14px;
line-height: 1.6;
}
.route-card {
display: grid;
gap: 8px;
padding: 16px;
border-radius: 24px;
background: linear-gradient(145deg, rgba(15, 118, 110, 0.92), rgba(20, 184, 166, 0.78));
color: #ecfeff;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 18px 34px rgba(15, 118, 110, 0.18);
}
.route-card small,
.phone-action small,
.phone-list-item small {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.14em;
opacity: 0.78;
}
.route-card strong {
font-size: 1.1rem;
line-height: 1.25;
letter-spacing: -0.03em;
}
.route-card span {
font-size: 14px;
line-height: 1.45;
opacity: 0.94;
}
.screen-kpis {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
}
.screen-kpi {
padding: 12px;
border-radius: 18px;
background: rgba(15, 23, 42, 0.05);
border: 1px solid rgba(15, 23, 42, 0.06);
display: grid;
gap: 4px;
}
.screen-kpi strong {
font-size: 1.1rem;
letter-spacing: -0.04em;
}
.screen-kpi span {
color: var(--muted);
font-size: 12px;
}
.phone-list {
display: grid;
gap: 10px;
}
.phone-list-item,
.phone-action,
.phone-empty {
border-radius: 22px;
padding: 14px 15px;
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(15, 23, 42, 0.08);
}
.phone-list-item strong,
.phone-action-title {
font-size: 0.98rem;
letter-spacing: -0.03em;
}
.phone-list-item span,
.phone-action-top span {
color: var(--accent-strong);
font-size: 12px;
font-weight: 800;
}
.phone-action {
display: grid;
gap: 10px;
color: inherit;
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.phone-action:hover {
transform: translateY(-1px);
box-shadow: 0 14px 30px rgba(15, 23, 42, 0.10);
}
.phone-action-meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.phone-action-meta span {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.05);
color: var(--text);
font-size: 12px;
font-weight: 700;
}
.phone-action--accent {
background: linear-gradient(180deg, rgba(236, 253, 245, 0.98), rgba(255, 255, 255, 0.96));
border-color: rgba(15, 118, 110, 0.18);
}
.phone-empty {
color: var(--muted);
font-size: 14px;
line-height: 1.6;
}
.form-card {
display: grid;
align-content: start;
gap: 16px;
}
.form-card form {
gap: 12px;
}
.form-card label {
padding: 12px;
border-radius: 20px;
border: 1px solid rgba(15, 23, 42, 0.08);
background: rgba(255, 255, 255, 0.74);
}
.field-label {
font-size: 11px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.form-card input,
.form-card select,
.form-card textarea {
padding: 13px 14px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.96);
}
.form-card .btn {
margin-top: 4px;
}
.form-note {
padding: 14px 16px;
border-radius: 20px;
background: rgba(15, 23, 42, 0.04);
border: 1px dashed rgba(15, 23, 42, 0.12);
color: var(--muted);
font-size: 14px;
line-height: 1.6;
}
.section-head {
margin-bottom: 16px;
flex-wrap: wrap;
align-items: end;
}
.section-head p {
max-width: 42ch;
}
.proof-stat {
font-size: clamp(2rem, 5vw, 2.8rem);
letter-spacing: -0.05em;
@ -466,10 +755,24 @@
}
.stats,
.grid-2 {
.grid-2,
.screen-kpis {
grid-template-columns: 1fr;
}
.phone-shell {
max-width: none;
width: 100%;
}
.phone-topbar,
.phone-list-row,
.phone-action-top,
.section-head {
align-items: flex-start;
flex-direction: column;
}
main { padding-top: 28px; }
.card { padding: 22px; }
.nav-links { gap: 12px; }

View File

@ -14,15 +14,28 @@
@endphp
<section class="split">
<article class="card offer-card">
<div class="offer-visual" aria-hidden="true"></div>
<div>
<span class="eyebrow">4 · Propuesta contextual</span>
<h1 style="font-size:clamp(2.15rem,4vw,3.6rem);max-width:14ch;">{{ $offer->title }}</h1>
<p>
{{ $offer->description ?: $offer->excerpt }}
Esta pantalla debe sentirse como una continuación natural del trayecto: útil, cercana y rápida de confirmar.
</p>
<div class="offer-meta" style="margin-top:16px;">
<div class="phone-screen" style="padding:18px;">
<div class="phone-topbar">
<span>Propuesta contextual</span>
<span class="phone-dot-group" aria-hidden="true">
<span class="phone-dot"></span>
<span class="phone-dot"></span>
<span class="phone-dot is-live"></span>
</span>
</div>
<div class="offer-visual" aria-hidden="true"></div>
<div>
<span class="screen-badge">4 · Propuesta contextual</span>
<h1 style="font-size:clamp(2.15rem,4vw,3.6rem);max-width:14ch;margin-top:14px;">{{ $offer->title }}</h1>
<p>
{{ $offer->description ?: $offer->excerpt }}
Esta pantalla debe sentirse como una continuación natural del trayecto: útil, cercana y rápida de confirmar.
</p>
</div>
<div class="offer-meta">
<span class="pill">{{ $categoryLabels[$offer->category] ?? ucfirst($offer->category) }}</span>
@if($offer->location_label)<span class="pill">{{ $offer->location_label }}</span>@endif
@if($offer->price_from)<span class="pill">Desde {{ number_format((float) $offer->price_from, 0) }}</span>@endif
@ -31,31 +44,31 @@
</div>
@if($recommendation)
<div class="notice" style="margin-top:18px;">
<div class="notice">
<strong>Por qué aparece ahora:</strong>
esta propuesta viene del trayecto hacia <strong>{{ $ride?->destination_label ?? ($offer->location_label ?: 'tu zona') }}</strong>
y quedó posicionada como recomendación #{{ $recommendation->position }} por su cercanía y facilidad de cierre.
</div>
@endif
<div class="stats" style="margin-top:18px;">
<div class="stat">
<div class="screen-kpis">
<div class="screen-kpi">
<strong>{{ $offer->location_label ?: 'Zona activa' }}</strong>
<span>cerca del destino o de la siguiente parada</span>
<span>cerca del destino</span>
</div>
<div class="stat">
<div class="screen-kpi">
<strong>{{ $offer->duration_minutes ? $offer->duration_minutes.' min' : 'Flexible' }}</strong>
<span>encaja mejor cuando la decisión es simple</span>
<span>decisión simple</span>
</div>
<div class="stat">
<div class="screen-kpi">
<strong>{{ $offer->price_from ? '€'.number_format((float) $offer->price_from, 0) : 'Consultar' }}</strong>
<span>ticket visible para atribución comercial</span>
<span>ticket trazable</span>
</div>
</div>
</div>
</article>
<aside class="card">
<aside class="card form-card">
<span class="eyebrow">5 · Reserva simple</span>
<h2>Confirma en menos de un minuto</h2>
<p>
@ -63,6 +76,10 @@
no solo intención.
</p>
<div class="form-note">
Este bloque funciona como checkout móvil mínimo: datos esenciales, intención clara y atribución al trayecto.
</div>
@if ($errors->any())
<div class="errors">
<ul>
@ -78,31 +95,31 @@
<input type="hidden" name="ride_id" value="{{ $ride?->id }}">
<input type="hidden" name="ride_recommendation_id" value="{{ $recommendation?->id }}">
<label>
Nombre
<span class="field-label">Nombre</span>
<input type="text" name="customer_name" value="{{ old('customer_name', 'Alex Morgan') }}" required>
</label>
<div class="grid-2">
<label>
Email
<span class="field-label">Email</span>
<input type="email" name="customer_email" value="{{ old('customer_email', 'alex@example.com') }}">
</label>
<label>
Teléfono
<span class="field-label">Teléfono</span>
<input type="text" name="customer_phone" value="{{ old('customer_phone', '+34 600 123 456') }}">
</label>
</div>
<div class="grid-2">
<label>
Personas
<span class="field-label">Personas</span>
<input type="number" name="party_size" value="{{ old('party_size', 2) }}" min="1" max="12" required>
</label>
<label>
Fecha / hora
<span class="field-label">Fecha / hora</span>
<input type="datetime-local" name="booking_for" value="{{ old('booking_for') }}">
</label>
</div>
<label>
Nota
<span class="field-label">Nota</span>
<textarea name="notes">{{ old('notes', 'Mesa tranquila si está disponible.') }}</textarea>
</label>
<button class="btn" type="submit">Confirmar reserva</button>

View File

@ -37,29 +37,48 @@
</div>
</article>
<aside class="card orb-wrap">
<div class="stack">
<div class="mini-card">
<small>Qué está pasando</small>
<h3>Momento de máxima intención</h3>
<p>El usuario ya resolvió movilidad. Ahora está mucho más abierto a una siguiente decisión simple y útil.</p>
<aside class="phone-shell" aria-label="Vista móvil del taxi confirmado">
<div class="phone-screen">
<div class="phone-topbar">
<span>Taxi confirmado</span>
<span class="phone-dot-group" aria-hidden="true">
<span class="phone-dot"></span>
<span class="phone-dot"></span>
<span class="phone-dot is-live"></span>
</span>
</div>
<div class="mini-card">
<small>Cómo decide el motor</small>
<h3>Cercanía + facilidad + timing</h3>
<p>Priorizamos propuestas que pueden encajar hoy, sin desviar demasiado la ruta ni pedir demasiada energía.</p>
<div class="route-card">
<small>Trayecto activo</small>
<strong>{{ $ride->pickup_label }} {{ $ride->destination_label }}</strong>
<span>ETA {{ $ride->eta_minutes ?? 6 }} min · {{ ucfirst($ride->source_channel) }} · {{ $ride->context_zone ?: 'Zona activa' }}</span>
</div>
<div class="mini-card success">
<small>Lo importante en la demo</small>
<h3>Taxi sugerencia clic</h3>
<p>El objetivo aquí no es fidelización compleja. Es demostrar que el trayecto activa demanda accionable.</p>
<span class="screen-badge">Encaja con tu ruta</span>
<div class="phone-list">
@forelse ($recommendations->take(3) as $recommendation)
<a class="phone-action {{ $recommendation->position === 1 ? 'phone-action--accent' : '' }}" href="{{ route('offers.show', $recommendation->offer->slug) }}?ride={{ $ride->id }}&recommendation={{ $recommendation->id }}">
<div class="phone-action-top">
<strong class="phone-action-title">{{ $recommendation->offer->title }}</strong>
<span>Top {{ $recommendation->position }}</span>
</div>
<small>{{ $recommendation->position === 1 ? 'Parada rápida' : 'Plan al llegar' }}</small>
<div class="phone-action-meta">
@if($recommendation->offer->location_label)<span>{{ $recommendation->offer->location_label }}</span>@endif
@if($recommendation->offer->duration_minutes)<span>{{ $recommendation->offer->duration_minutes }} min</span>@endif
@if($recommendation->offer->price_from)<span>{{ number_format((float) $recommendation->offer->price_from, 0) }}</span>@endif
</div>
</a>
@empty
<div class="phone-empty">No hay recomendaciones activas todavía, pero el ride ya quedó listo para atribución.</div>
@endforelse
</div>
</div>
</aside>
</section>
<section class="section">
<div style="display:flex;justify-content:space-between;align-items:end;gap:16px;flex-wrap:wrap;margin-bottom:16px;">
<div class="section-head">
<div>
<span class="eyebrow">3 · Recomendaciones</span>
<h2>Encajan con tu ruta ahora</h2>