Files
padelnomics/web/src/padelnomics/static/css/input.css
Deeman 165eaf48bf fix(admin): live search not firing on text input + spinner always visible
hx-trigger bug:
  "from:find input" in hx-trigger attaches the event listener to the
  first <input> found in the form — which is the hidden CSRF token input.
  Typing in the visible search field never fires the listener on that
  element. Result: only Enter (form submit) triggered HTMX.
  Fix: drop "from:find input" so the listener is on the form itself,
  where input/change events from all children bubble naturally.

Spinner visibility bug:
  .search-spinner { opacity: 0 } relied on our compiled output.css.
  HTMX ships its own built-in CSS for .htmx-indicator (opacity:0 →
  opacity:1 on htmx-request). Using class="htmx-indicator search-spinner"
  delegates hide/show to HTMX's own stylesheet with no dependency on
  whether output.css has been rebuilt. Our .search-spinner only handles
  positioning and the spin animation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:09:49 +01:00

598 lines
16 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@import "tailwindcss";
/* ── Commit Mono (self-hosted) ── */
@font-face {
font-family: "Commit Mono";
src: url("../fonts/CommitMono-400-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Commit Mono";
src: url("../fonts/CommitMono-700-Regular.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* ── Bricolage Grotesque (self-hosted, variable font) ── */
@font-face {
font-family: 'Bricolage Grotesque';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('../fonts/bricolage-grotesque-vietnamese.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Bricolage Grotesque';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('../fonts/bricolage-grotesque-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Bricolage Grotesque';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('../fonts/bricolage-grotesque-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ── DM Sans (self-hosted, variable font) ── */
@font-face {
font-family: 'DM Sans';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url('../fonts/dm-sans-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'DM Sans';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url('../fonts/dm-sans-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ── Brand Theme ── */
@theme {
--font-display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif;
--font-sans: "DM Sans", ui-sans-serif, system-ui, -apple-system, sans-serif;
--font-mono: "Commit Mono", ui-monospace, monospace;
--color-navy: #0F172A;
--color-charcoal: #1E293B;
--color-electric: #1D4ED8;
--color-electric-hover: #1E40AF;
--color-accent: #16A34A;
--color-forest: #064E3B;
--color-soft-white: #F8FAFC;
--color-light-gray: #E2E8F0;
--color-mid-gray: #CBD5E1;
--color-slate: #64748B;
--color-slate-dark: #475569;
--color-danger: #EF4444;
--color-danger-hover: #DC2626;
--color-warning: #D97706;
}
/* ── Base layer ── */
@layer base {
body {
@apply bg-soft-white text-slate-dark font-sans antialiased;
}
h1, h2, h3 {
font-family: var(--font-display);
@apply text-navy font-bold tracking-tight;
}
h4, h5, h6 {
@apply text-charcoal font-semibold;
}
a {
@apply text-electric hover:text-electric-hover transition-colors;
}
hr {
@apply border-light-gray my-6;
}
}
/* ── Component classes ── */
@layer components {
/* ── Navigation ── */
.nav-bar {
position: sticky;
top: 0;
z-index: 50;
background: #fff;
border-bottom: 1px solid #E2E8F0;
}
.nav-inner {
max-width: 72rem; /* matches container-page (max-w-6xl) */
margin: 0 auto;
padding: 0 1rem; /* matches px-4 (mobile) */
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
height: 56px;
}
@media (min-width: 640px) {
.nav-inner { padding-inline: 1.5rem; } /* matches sm:px-6 */
}
@media (min-width: 1024px) {
.nav-inner { padding-inline: 2rem; } /* matches lg:px-8 */
}
.nav-logo {
flex-shrink: 0;
padding: 0 1rem; /* 16px, matches Zillow logo left/right padding */
}
.nav-links {
display: flex;
align-items: center;
gap: 1.25rem;
font-size: 0.875rem;
font-weight: 500;
}
.nav-links a {
color: #475569;
text-decoration: none;
transition: color 0.15s;
}
.nav-links a:hover {
color: #1D4ED8;
}
.nav-links--left { flex: 1; justify-content: flex-start; }
.nav-links--right { flex: 1; justify-content: flex-end; }
a.nav-auth-btn,
button.nav-auth-btn {
display: inline-flex;
align-items: center;
white-space: nowrap;
padding: 6px 16px;
border: none;
border-radius: 10px;
font-size: 0.8125rem;
font-weight: 600;
color: #fff;
background: #1D4ED8;
cursor: pointer;
text-decoration: none;
box-shadow: 0 2px 8px rgba(29,78,216,0.25);
transition: background 0.15s;
}
a.nav-auth-btn:hover,
button.nav-auth-btn:hover {
background: #1E40AF;
color: #fff;
}
.nav-badge {
@apply bg-electric/10 text-electric px-2 py-0.5 text-xs font-semibold rounded-full;
}
.nav-form {
margin: 0;
padding: 0;
display: inline;
}
/* Hamburger button — hidden on desktop, shown on mobile (left) */
.nav-hamburger {
display: none;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: transparent;
border: none;
cursor: pointer;
padding: 6px;
border-radius: 8px;
transition: background 0.15s;
}
.nav-hamburger:hover { background: #F1F5F9; }
.nav-hamburger svg { display: block; }
/* Mobile-only auth slot (right side) — hidden on desktop */
.nav-auth-mobile {
display: none;
align-items: center;
justify-content: flex-end;
min-width: 36px; /* mirrors hamburger width so logo stays centred when empty */
}
/* Mobile menu panel — hidden by default */
.nav-mobile {
display: none;
position: absolute;
top: 56px;
left: 0;
right: 0;
background: #fff;
border-bottom: 1px solid #E2E8F0;
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
z-index: 49;
flex-direction: column;
padding: 0.75rem 1.5rem 1rem;
}
.nav-bar[data-navopen="true"] .nav-mobile {
display: flex;
}
.nav-mobile a,
.nav-mobile button.nav-auth-btn,
.nav-mobile a.nav-auth-btn {
display: block;
padding: 0.625rem 0;
border-bottom: 1px solid #F1F5F9;
font-size: 0.9375rem;
font-weight: 500;
color: #475569;
text-decoration: none;
transition: color 0.15s;
}
.nav-mobile a:last-child { border-bottom: none; }
.nav-mobile a:hover { color: #1D4ED8; }
.nav-mobile a.nav-auth-btn,
.nav-mobile button.nav-auth-btn {
display: inline-flex;
margin-top: 0.5rem;
border-bottom: none;
width: auto;
align-self: flex-start;
}
.nav-mobile .nav-mobile-section {
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #94A3B8;
padding: 0.75rem 0 0.25rem;
}
/* Overlay behind mobile menu */
.nav-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 48;
}
.nav-bar[data-navopen="true"] ~ .nav-overlay {
display: block;
}
@media (max-width: 899px) {
.nav-links { display: none; }
.nav-hamburger { display: flex; }
.nav-auth-mobile { display: flex; }
/* Mobile grid: [hamburger] [logo] [sign-in] — hamburger left, sign-in right */
.nav-inner { grid-template-columns: auto 1fr auto; }
.nav-logo { justify-self: center; }
.nav-bar { position: sticky; }
}
/* Page container */
.container-page {
@apply max-w-6xl mx-auto px-4 sm:px-6 lg:px-8;
}
/* Cards (replace Pico <article>) */
.card {
@apply bg-white border border-light-gray rounded-2xl p-6 mb-6 shadow-sm;
}
.card-header {
@apply border-b border-light-gray pb-3 mb-4 text-sm text-slate font-medium;
}
/* Buttons — shared base */
.btn, .btn-secondary, .btn-danger {
@apply inline-flex items-center justify-center px-5 py-2.5 rounded-xl
font-semibold text-sm transition-colors cursor-pointer
focus:outline-none focus:ring-2 focus:ring-electric/50;
}
.btn {
@apply bg-electric text-white hover:bg-electric-hover shadow-[0_2px_10px_rgba(29,78,216,0.25)];
}
.btn-secondary {
@apply bg-slate-dark text-white hover:bg-navy;
}
.btn-danger {
@apply bg-danger text-white hover:bg-danger-hover;
}
.btn-outline {
@apply inline-flex items-center justify-center px-5 py-2.5 rounded-xl
font-semibold text-sm transition-colors cursor-pointer
bg-transparent text-slate-dark border border-mid-gray
hover:bg-light-gray hover:text-navy
focus:outline-none focus:ring-2 focus:ring-electric/50;
}
.btn-sm {
@apply px-3 py-1.5 text-xs;
}
/* Forms */
.form-label {
@apply block text-sm font-medium text-charcoal mb-1;
}
.form-input {
@apply w-full px-3 py-2 rounded-xl border border-mid-gray bg-white
text-slate-dark placeholder-slate
focus:outline-none focus:ring-2 focus:ring-electric/50 focus:border-electric
transition-colors;
}
.form-hint {
@apply text-xs text-slate mt-1;
}
/* Tables */
.table {
@apply w-full text-sm;
}
.table th {
@apply text-left px-3 py-2 text-xs font-semibold text-slate uppercase tracking-wider
border-b-2 border-light-gray;
}
.table td {
@apply px-3 py-2 border-b border-light-gray text-slate-dark;
}
.table tbody tr:hover {
@apply bg-soft-white;
}
/* Flash messages */
.flash, .flash-error, .flash-success, .flash-warning {
@apply px-4 py-3 rounded-xl mb-4 border-l-4 bg-white text-slate-dark text-sm;
}
.flash {
@apply border-electric;
}
.flash-error {
@apply border-danger;
}
.flash-success {
@apply border-accent;
}
.flash-warning {
@apply border-warning;
}
/* Badge (replaces Pico <mark>) */
.badge, .badge-success, .badge-danger, .badge-warning {
@apply inline-block px-2 py-0.5 text-xs font-semibold rounded-full;
}
.badge {
@apply bg-electric/10 text-electric;
}
.badge-success {
@apply bg-accent/10 text-accent;
}
.badge-danger {
@apply bg-danger/10 text-danger;
}
.badge-warning {
@apply bg-warning/10 text-warning;
}
/* Heading group (replaces Pico <hgroup>) */
.heading-group {
@apply mb-6;
}
.heading-group h1,
.heading-group h2 {
@apply mb-1;
}
.heading-group p {
@apply text-slate text-lg;
}
/* Grid helpers (replaces Pico .grid) */
.grid-auto {
@apply grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6;
}
.grid-2 {
@apply grid grid-cols-1 md:grid-cols-2 gap-6;
}
.grid-3 {
@apply grid grid-cols-1 md:grid-cols-3 gap-6;
}
.grid-4 {
@apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6;
}
.grid-5 {
@apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-6;
}
/* Monospace data display */
.metric {
@apply font-mono text-navy;
}
.mono {
@apply font-mono;
}
/* HTMX loading indicators */
.htmx-indicator {
display: none;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
display: inline;
}
/* ── Scenario Widgets ── */
.scenario-widget {
@apply border border-light-gray rounded-2xl overflow-hidden mb-6 bg-white shadow-sm;
}
.scenario-widget__header {
@apply flex justify-between items-center px-4 py-3 text-sm font-semibold text-white;
background: var(--color-navy);
}
.scenario-widget__location {
@apply uppercase tracking-wider text-xs;
}
.scenario-widget__config {
@apply text-xs font-normal opacity-80;
}
.scenario-widget__body {
@apply p-4;
}
.scenario-widget__metrics {
@apply grid gap-4 mb-4;
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 640px) {
.scenario-widget__metrics {
grid-template-columns: repeat(2, 1fr);
}
}
.scenario-widget__metric {
@apply text-center;
}
.scenario-widget__metric-label {
@apply block text-xs text-slate mb-0.5;
}
.scenario-widget__metric-value {
@apply block text-lg font-bold font-mono text-navy;
}
.scenario-widget__section-title {
@apply text-sm font-semibold text-charcoal mb-2 mt-4 pb-1 border-b border-light-gray;
}
.scenario-widget__table-wrap {
@apply overflow-x-auto;
}
.scenario-widget__table {
@apply w-full text-sm;
}
.scenario-widget__table th {
@apply text-left px-3 py-1.5 text-xs font-semibold text-slate uppercase tracking-wider border-b border-light-gray;
}
.scenario-widget__table td {
@apply px-3 py-1.5 border-b border-light-gray text-slate-dark;
}
.scenario-widget__table tfoot td,
.scenario-widget__table tfoot th {
@apply border-t-2 border-light-gray font-semibold;
}
.scenario-widget__cta {
@apply px-4 py-3 text-center border-t border-light-gray bg-soft-white;
}
.scenario-widget__cta a {
@apply text-sm font-semibold text-electric hover:text-electric-hover;
}
/* ── Article Body Typography ── */
/* ── Stats Strip — pSEO article hero metrics ── */
/* Baked into static HTML at generation time; no JS needed. */
.stats-strip {
@apply grid grid-cols-2 gap-3 mb-8;
}
@media (min-width: 640px) {
.stats-strip { grid-template-columns: repeat(4, 1fr); }
}
.stats-strip__item {
@apply bg-soft-white border border-light-gray rounded-lg p-4;
}
.stats-strip__label {
@apply text-xs text-slate uppercase tracking-wide mb-1;
}
.stats-strip__value {
@apply text-2xl font-extrabold text-navy;
font-family: 'Bricolage Grotesque', sans-serif;
}
.stats-strip__unit {
@apply text-sm text-slate font-normal ml-1;
}
/* ── Article Body Typography ── */
.article-body h2 {
@apply text-2xl mt-10 mb-4;
}
.article-body h3 {
@apply text-xl mt-8 mb-3;
}
.article-body h4 {
@apply text-lg mt-6 mb-2;
}
.article-body p {
@apply text-base leading-relaxed mb-4 text-slate-dark;
}
.article-body ul, .article-body ol {
@apply mb-4 pl-6;
}
.article-body ul {
@apply list-disc;
}
.article-body ol {
@apply list-decimal;
}
.article-body li {
@apply mb-1 text-slate-dark;
}
.article-body blockquote {
@apply border-l-4 border-electric pl-4 italic text-slate my-6;
}
.article-body table {
@apply w-full text-sm mb-6;
}
.article-body table th {
@apply text-left px-3 py-2 text-xs font-semibold text-slate uppercase border-b-2 border-light-gray;
}
.article-body table td {
@apply px-3 py-2 border-b border-light-gray text-slate-dark;
}
.article-body code {
@apply font-mono text-sm bg-light-gray px-1 py-0.5 rounded;
}
.article-body pre {
@apply bg-navy text-white p-4 rounded-xl mb-4 overflow-x-auto;
}
.article-body pre code {
@apply bg-transparent p-0;
}
.article-body a {
@apply text-electric underline hover:text-electric-hover;
}
.article-body details {
@apply border border-light-gray rounded-lg mb-3 overflow-hidden;
}
.article-body details summary {
@apply px-4 py-3 font-semibold text-navy cursor-pointer select-none;
list-style: none;
}
.article-body details summary::-webkit-details-marker { display: none; }
.article-body details summary::after {
content: '+';
@apply float-right text-slate font-normal;
}
.article-body details[open] summary::after {
content: '';
}
.article-body details > p,
.article-body details > div {
@apply px-4 pb-4 text-slate-dark;
}
/* Inline HTMX loading indicator for search forms.
Opacity is handled by HTMX's built-in .htmx-indicator CSS.
This class only adds positioning and the spin animation. */
.search-spinner {
flex-shrink: 0;
align-self: center;
}
.search-spinner.htmx-request {
animation: spin-icon 0.9s linear infinite;
}
/* Article generation spinner banner */
.generating-banner {
@apply flex items-center gap-3 rounded-xl border border-light-gray bg-white text-sm text-slate-dark mb-4;
padding: 0.75rem 1rem;
}
.spinner-icon {
flex-shrink: 0;
animation: spin-icon 0.9s linear infinite;
}
}
@keyframes spin-icon {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}