towerops/lib/towerops_web/components/marketing_layouts.ex
Graham McIntire b1cbdc6c28 fix: serve logo as WebP with PNG fallback via picture element
Converts the 120KB PNG logo to 17KB WebP (86% smaller). Both logo instances in the marketing layout use <picture> with WebP source and PNG fallback for maximum browser compatibility.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 09:53:54 -05:00

172 lines
6.4 KiB
Elixir

defmodule ToweropsWeb.MarketingLayouts do
@moduledoc """
Marketing layouts for public-facing pages.
"""
use ToweropsWeb, :html
alias ToweropsWeb.Components.CookieConsent
@doc """
Renders the marketing layout.
Use this for public-facing marketing pages.
## Examples
<MarketingLayouts.marketing flash={@flash}>
<h1>Marketing Content</h1>
</MarketingLayouts.marketing>
"""
attr :flash, :map, required: true, doc: "the map of flash messages"
slot :inner_block, required: true
def marketing(assigns) do
assigns = Map.put_new(assigns, :requires_cookie_consent, false)
~H"""
<div class="bg-white dark:bg-slate-950">
<header class="py-10">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<nav class="relative z-50 flex justify-between">
<div class="flex items-center md:gap-x-12 dark:bg-slate-950 dark:rounded-lg">
<.link navigate={~p"/"} aria-label="Home">
<picture>
<source srcset="/images/towerops_logo.webp" type="image/webp" />
<img
src="/images/towerops_logo.png"
alt="Towerops"
class="w-48 h-auto sm:w-64 dark:invert dark:mix-blend-screen"
/>
</picture>
</.link>
</div>
<div class="flex items-center gap-x-5 md:gap-x-8">
<div class="hidden md:block">
<.link
navigate={~p"/users/log-in"}
class="inline-block rounded-lg px-2 py-1 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-white"
>
Sign in
</.link>
</div>
<.link
navigate={~p"/users/register"}
class="group inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 bg-blue-600 text-white hover:text-slate-100 hover:bg-blue-500 active:bg-blue-800 active:text-blue-100 focus-visible:outline-blue-600"
>
<span>
Get started <span class="hidden lg:inline">today</span>
</span>
</.link>
</div>
</nav>
</div>
</header>
<main>
{render_slot(@inner_block)}
</main>
<footer class="bg-slate-50 dark:bg-slate-900">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="py-16">
<picture>
<source srcset="/images/towerops_logo.webp" type="image/webp" />
<img
src="/images/towerops_logo.png"
alt="Towerops"
class="mx-auto w-48 h-auto sm:w-64 dark:invert dark:mix-blend-screen"
/>
</picture>
</div>
<div class="grid grid-cols-1 gap-8 border-t border-slate-400/10 py-10 lg:grid-cols-4">
<!-- Product Column -->
<div>
<h3 class="text-sm font-semibold text-slate-900 dark:text-white mb-4">Product</h3>
<ul class="space-y-3">
<li>
<.link
href="/#features"
class="text-sm text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
Features
</.link>
</li>
<li>
<.link
href="/#pricing"
class="text-sm text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
Pricing
</.link>
</li>
</ul>
</div>
<!-- Developers Column -->
<div>
<h3 class="text-sm font-semibold text-slate-900 dark:text-white mb-4">Developers</h3>
<ul class="space-y-3">
<li>
<.link
navigate={~p"/docs/api"}
class="text-sm text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
API Documentation
</.link>
</li>
<li>
<.link
navigate={~p"/docs/graphql"}
class="text-sm text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
GraphQL API
</.link>
</li>
<li>
<.link
navigate={~p"/help"}
class="text-sm text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white transition-colors"
>
Help & Docs
</.link>
</li>
</ul>
</div>
<!-- Copyright and Legal -->
<div class="lg:col-span-2">
<div class="flex flex-col items-start gap-4 sm:flex-row sm:justify-between sm:items-end">
<div class="flex flex-col gap-2">
<p class="text-sm text-slate-500 dark:text-slate-400">
Copyright © {Date.utc_today().year} Towerops
</p>
<div class="flex gap-x-4 text-xs text-slate-500 dark:text-slate-500">
<.link
navigate={~p"/privacy"}
class="hover:text-slate-700 dark:hover:text-slate-300 underline decoration-dotted underline-offset-2"
>
Privacy Policy
</.link>
<span>·</span>
<.link
navigate={~p"/terms"}
class="hover:text-slate-700 dark:hover:text-slate-300 underline decoration-dotted underline-offset-2"
>
Terms of Service
</.link>
</div>
</div>
</div>
</div>
</div>
</div>
</footer>
</div>
<Layouts.flash_group flash={@flash} />
<CookieConsent.banner requires_consent={@requires_cookie_consent} />
"""
end
end