/* ================================
   SANTOS ARMADA — Base Styles
   An Army of Knowledge
================================ */

@import url('vendor/fonts/style-fonts.css');

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

@font-face {
  font-family: 'Playfair Display';
  src: url('/fonts/PlayfairDisplay-VariableFont_wght.ttf') format('truetype');
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

:root {
    --bg-dark:        #0a0805;
    --bg-card:        #110e09;
    --gold:           #c8a96e;
    --gold-light:     #e8d5a3;
    --gold-dim:       rgba(200, 169, 110, 0.4);
    --text-primary:   #e8d5a3;
    --text-secondary: rgba(232, 213, 163, 0.55);
    --border:         rgba(200, 169, 110, 0.15);
    --font-title:     'Cinzel', serif;
    --font-body:      'EB Garamond', serif;
}

body {
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: 18px;
    line-height: 1.7;
    min-height: 100vh;
}
.page-content {
    position: relative;
    z-index: 1;
}

/* --- Maya glyph banner strip (top + bottom) ---
   Reusable version of the fixed frieze strip built for the Museos page.
   Permanent fixtures: pinned to the viewport edges with position:fixed
   so they never scroll away, sitting above the navbar and below the
   footer. Add class="has-site-banners" to <body>, drop a
   .site-banner.site-banner-top and .site-banner.site-banner-bottom
   section in (top one as the very first child of <body>, bottom one as
   the last), and this handles the rest — including reserving room in
   the normal flow so nothing loads hidden underneath them, and docking
   the sticky navbar right below the top strip instead of at the very
   top of the viewport. */
body.has-site-banners {
    --site-banner-h: 41px;
    padding-top: var(--site-banner-h);
    padding-bottom: var(--site-banner-h);
}

.site-banner {
    position: fixed;
    left: 0;
    width: 100%;
    height: var(--site-banner-h, 41px);
    z-index: 200;
    background: #000;
    overflow: hidden;
}

.site-banner-top {
    top: 0;
}

.site-banner-bottom {
    bottom: 0;
}

.site-banner-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

body.has-site-banners .navbar {
    top: var(--site-banner-h);
}

@media (max-width: 768px) {
    body.has-site-banners {
        --site-banner-h: 27px;
    }
}

/* Below 768px, and on tablets in portrait up to 1024px, the navbar
   switches from sticky to position:relative, where `top` would just
   shove it further down inside the normal flow on top of the
   padding-top above. Cancel that offset in both cases since
   padding-top alone already reserves the space for the fixed banner. */
@media (max-width: 768px), (orientation: portrait) and (max-width: 1024px) {
    body.has-site-banners .navbar {
        top: 0;
    }
}

/* --- Navbar --- */
.navbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 48px;
    border-bottom: 0.5px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: rgba(10, 8, 5, 0.95);
    backdrop-filter: blur(8px);
}

.nav-logo {
    display: flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
}

.nav-crest {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    border: 0.5px solid var(--gold-dim);
    object-fit: contain;
    filter: invert(1) sepia(1) saturate(1.5) hue-rotate(5deg) brightness(0.85);
}

.nav-title {
    font-family: var(--font-title);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.16em;
    color: var(--gold);
}

.nav-links {
    display: flex;
    list-style: none;
    gap: 32px;
}

.nav-links a {
    font-family: var(--font-title);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.1em;
    color: var(--text-secondary);
    text-decoration: none;
    text-transform: uppercase;
    transition: color 0.2s ease;
}

.nav-links a:hover {
    color: var(--gold);
}
/* --- Hero --- */
.hero {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 60px 48px 0;
    text-align: center;
}

.hero-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 24px;
    max-width: 900px;
    position: relative;
    z-index: 1;
}

#three-container {
    width: 100%;
    height: 180px;
    position: relative;
}

#three-container canvas {
    width: 100% !important;
    height: 100% !important;
}

.hero-tagline {
    font-family: var(--font-title);
    font-size: 17px;
    letter-spacing: 0.2em;
    color: var(--text-secondary);
    text-transform: uppercase;
}

.hero-title {
    font-family: "Playfair Display", serif;
    font-size: 86px;
    font-weight: 400;
    color: var(--gold-light);
    letter-spacing: 0.12em;
}

.hero-title-effect {
    display: inline-flex;
}

.hero-title .char {
    display: inline-block;
    white-space: pre;
    will-change: transform, opacity, background;
}

.hero-image-wrap {
    position: relative;
    width: 100%;
    max-width: 820px;
}

.hero-image {
    display: block;
    width: 100%;
    border: 0.5px solid var(--border);
}

.hero-sparks-canvas {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
    z-index: 2;
}

.hero-sub {
    font-family: var(--font-title);
    font-size: 13px;
    letter-spacing: 0.18em;
    color: var(--gold);
    text-transform: uppercase;
}
/* --- Singularity Panel --- */
#singularity-panel {
    position: relative;
    width: 100%;
    height: 70vh;
    overflow: hidden;
    background: transparent;
}

#singularity-canvas-wrap {
    position: absolute;
    inset: 0;
}

@media (max-width: 768px) {
    #singularity-panel {
        height: 50vh;
    }
}
/* --- Quotes --- */
.quotes {
    padding: 60px 48px;
    border-top: 0.5px solid var(--border);
    border-bottom: 0.5px solid var(--border);
    background-color: transparent;
}

.quotes-inner {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 40px;
    max-width: 1200px;
    margin: 0 auto;
}

.quote-item {
    border-left: 1px solid var(--gold-dim);
    padding-left: 20px;
}

.quote-item p {
    font-family: var(--font-body);
    font-style: italic;
    font-size: 16px;
    color: var(--text-primary);
    line-height: 1.8;
    margin-bottom: 12px;
}

.quote-item cite {
    font-family: var(--font-title);
    font-size: 11px;
    letter-spacing: 0.12em;
    color: var(--gold);
    font-style: normal;
}
/* --- Sections Grid --- */
.sections-grid {
    padding: 80px 48px;
    max-width: 1200px;
    margin: 0 auto;
    background: transparent;
}

.grid-header {
    text-align: center;
    margin-bottom: 56px;
}

.grid-title {
    font-family: var(--font-title);
    font-size: 28px;
    font-weight: 700;
    color: var(--gold-light);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: 14px;
    overflow: hidden;
    padding-bottom: 0.15em;
}

.grid-title .ts-word {
    display: inline-block;
    transform: translateY(110%);
    opacity: 0;
}

.grid-title.is-revealed .ts-word {
    animation: word-rise 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) forwards,
               word-glow 1.4s ease forwards;
}

@keyframes word-rise {
    to { transform: translateY(0); opacity: 1; }
}

@keyframes word-glow {
    0%   { text-shadow: 0 0 0 rgba(200, 169, 110, 0); }
    35%  { text-shadow: 0 0 18px rgba(232, 213, 163, 0.85); }
    100% { text-shadow: 0 0 0 rgba(200, 169, 110, 0); }
}

.grid-sub {
    font-family: var(--font-body);
    font-style: italic;
    font-size: 16px;
    color: var(--text-secondary);
}

.grid-inner {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 20px;
}

.grid-card {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border: 0.5px solid var(--border);
    text-decoration: none;
    display: block;
    isolation: isolate;
}

.grid-card img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: grayscale(55%) sepia(20%) brightness(0.75) contrast(1.05);
    transform: scale(1.04);
    transition: filter 0.7s cubic-bezier(0.2, 0.8, 0.2, 1), transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
    z-index: -2;
}

.grid-card::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(10,8,5,0.05) 0%, rgba(10,8,5,0.45) 58%, rgba(10,8,5,0.94) 100%);
    z-index: -1;
    transition: background 0.7s ease;
}

.grid-card:hover img {
    filter: grayscale(0%) sepia(0%) brightness(0.9) contrast(1);
    transform: scale(1.1);
}

.grid-globe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: -2;
    overflow: hidden;
    opacity: 0;
    filter: grayscale(55%) sepia(20%) brightness(0.75) contrast(1.05);
    transform: scale(1.04);
    transition: filter 0.7s cubic-bezier(0.2, 0.8, 0.2, 1), transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.8s ease;
}

.grid-globe.is-ready {
    opacity: 1;
}

.grid-globe canvas {
    display: block;
    pointer-events: none;
}

.grid-card:hover .grid-globe {
    filter: grayscale(0%) sepia(0%) brightness(0.9) contrast(1);
    transform: scale(1.1);
}

.grid-card:hover::after {
    background: linear-gradient(180deg, rgba(10,8,5,0.0) 0%, rgba(10,8,5,0.35) 58%, rgba(10,8,5,0.92) 100%);
}

.card-mark {
    position: absolute;
    top: 14px;
    right: 16px;
    font-size: 16px;
    opacity: 0.75;
    filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));
}

.card-text {
    position: absolute;
    left: 18px;
    right: 18px;
    bottom: 16px;
}

.grid-card h3 {
    font-family: var(--font-title);
    font-size: 14px;
    font-weight: 500;
    color: var(--gold-light);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    margin-bottom: 4px;
    transition: color 0.3s ease;
}

.grid-card:hover h3 {
    color: var(--gold);
}

.grid-card p {
    font-family: var(--font-body);
    font-style: italic;
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.4;
}

/* --- Libritos card: shooting stars confined to the sliver of dusk sky
   visible between the foreground archway and the pyramid's roofline
   in Images/grid/libritos.jpg. The clip-path traces that gap, this
   time measured programmatically (scanned the actual source photo
   pixel-by-pixel for the purple-sky/tan-stone color boundary, since
   two rounds of eyeballing it -- first too cramped at y<=31%, then
   too wide because dark shadow between the stone blocks near the top
   of frame was mistaken for a narrow sky slot -- both missed). Real
   sky doesn't start until y=~18% (everything above that, including
   what looked like a slot at the very top, is shadowed stone) and
   stays one continuous region through y=~48%, where the pyramid's
   own silhouette starts splitting it into two narrower side slivers
   -- not included here, kept to the one clean continuous region. So
   the stars -- and their full animated paths -- only ever render
   inside that sky, never over the stone, no matter how the paths
   below are tuned. Three stars on three shallow near-horizontal streaks,
   staggered so one is always mid-flight, read as a continuous stream
   rather than a single star repeating with a gap. --- */
.libritos-sky {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    clip-path: polygon(
        43.0% 18%, 54.6% 18%,
        54.4% 24%, 58.3% 30%, 65.3% 36%, 72.7% 42%, 79.9% 48%,
        20.9% 48%, 27.4% 42%, 34.7% 36%, 41.4% 30%, 44.0% 24%
    );
}

.libritos-shooting-star {
    position: absolute;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 6px 1px rgba(255, 255, 255, 0.9);
    animation: libritos-star-streak-1 3s linear infinite;
}

.libritos-shooting-star:nth-child(2) {
    animation-name: libritos-star-streak-2;
    animation-delay: 1s;
}

.libritos-shooting-star:nth-child(3) {
    animation-name: libritos-star-streak-3;
    animation-delay: 2s;
}

.libritos-shooting-star::before {
    content: '';
    position: absolute;
    top: 50%;
    right: 100%;
    width: 46px;
    height: 1.5px;
    background: linear-gradient(to left, rgba(255, 255, 255, 0.8), transparent);
    transform: translateY(-50%) rotate(180deg) skewY(4deg);
    transform-origin: right center;
}

@keyframes libritos-star-streak-1 {
    0%   { left: 38%; top: 35%;   opacity: 0; }
    8%   { opacity: 1; }
    92%  { opacity: 1; }
    100% { left: 62%; top: 37.5%; opacity: 0; }
}

@keyframes libritos-star-streak-2 {
    0%   { left: 32%; top: 41%;   opacity: 0; }
    8%   { opacity: 1; }
    92%  { opacity: 1; }
    100% { left: 68%; top: 43.5%; opacity: 0; }
}

@keyframes libritos-star-streak-3 {
    0%   { left: 27%; top: 46%;   opacity: 0; }
    8%   { opacity: 1; }
    92%  { opacity: 1; }
    100% { left: 73%; top: 47.5%; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .libritos-shooting-star {
        animation: none;
        opacity: 0;
    }
}

/* --- Interpreter Z card: pulsing red glow confined to wherever the
   torn-paper Z's red pixels actually are in Images/grid/interpreter-z.jpg
   -- without an external mask (mask-image proved unreliable to verify
   across browsers here). Instead this layers a brightness/saturation-
   boosted, slightly blurred copy of the SAME photo on top via
   background-image + mix-blend-mode:screen. Screening a brightened
   copy of black pixels over black is still black (any multiplier x 0
   is 0), so the surrounding background is mathematically guaranteed
   to stay untouched -- only the already-red Z gets pushed brighter,
   with the blur giving it a soft glowing bleed right at its own
   edges instead of a hard-edged rectangle. */
.interpreter-z-glow {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: url(Images/grid/interpreter-z.jpg);
    background-size: cover;
    background-position: center;
    mix-blend-mode: screen;
    animation: interpreter-z-pulse 2.2s ease-in-out infinite;
}

@keyframes interpreter-z-pulse {
    0%, 100% { filter: brightness(1.5) saturate(1.6) blur(2px); opacity: 0.55; }
    50%      { filter: brightness(2.8) saturate(2.2) blur(4px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .interpreter-z-glow {
        animation: none;
        filter: brightness(2) saturate(1.8) blur(3px);
        opacity: 0.85;
    }
}

/* --- Mercadeo card: synthetic flickering candle flame over the
   unlit candle in Images/grid/mercadeo.jpg. There's no actual flame
   pixel color in the source photo to amplify (unlike the Interpreter
   Z glow), so this paints one on: a small warm radial-gradient flame
   shape positioned at the wick tip (measured by eye against a 3x
   zoomed crop of the source photo), with an irregular multi-step
   flicker -- uneven opacity/scale/rotation steps rather than one
   smooth pulse -- so it reads as a live flame, not a beacon. --- */
.mercadeo-flame {
    position: absolute;
    left: 49.3%;
    top: 4.5%;
    width: 3%;
    height: 4.5%;
    pointer-events: none;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    background: radial-gradient(ellipse at 50% 65%, #fff9d6 0%, #ffd23f 25%, #ff9d1f 50%, #ff5a1f 72%, transparent 85%);
    filter: blur(1px) drop-shadow(0 0 5px rgba(255, 150, 30, 0.8)) drop-shadow(0 0 10px rgba(255, 100, 20, 0.5));
    transform-origin: 50% 90%;
    animation: mercadeo-flicker 1.6s ease-in-out infinite;
}

@keyframes mercadeo-flicker {
    0%   { opacity: 0.85; transform: scale(1, 1) rotate(0deg); }
    8%   { opacity: 0.7;  transform: scale(0.9, 1.05) rotate(-3deg); }
    18%  { opacity: 1;    transform: scale(1.08, 0.95) rotate(2deg); }
    29%  { opacity: 0.78; transform: scale(0.94, 1.02) rotate(-2deg); }
    41%  { opacity: 1;    transform: scale(1.1, 1.08) rotate(3deg); }
    53%  { opacity: 0.72; transform: scale(0.88, 0.92) rotate(-4deg); }
    67%  { opacity: 0.95; transform: scale(1.02, 1.03) rotate(1deg); }
    79%  { opacity: 0.85; transform: scale(0.95, 1) rotate(2deg); }
    91%  { opacity: 1;    transform: scale(1.05, 0.96) rotate(-1deg); }
    100% { opacity: 0.85; transform: scale(1, 1) rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
    .mercadeo-flame {
        animation: none;
        opacity: 0.9;
    }
}

/* --- Curriculum Vitae card: shimmering gold "GV" monogram. Unlike
   the Interpreter Z glow (confined for free by a pure-black backdrop)
   this plaque sits on mid-tone orange, so simply screening a
   brightened copy of the whole photo would light up the background
   almost as much as the letters. And unlike the earlier attempt at
   this same problem, CSS mask-image proved unreliable in real-world
   testing here (confirmed: a masked box rendered as a plain
   unmasked rectangle), so this doesn't use masking either.

   Instead, Images/grid/cv-letters.png is a real RGBA cutout -- actual
   transparency baked into the file itself, not a live CSS mask -- of
   just the "GV" letters, extracted by scanning the source photo for
   where it deviates from the background's characteristic color
   (bright specular highlights, dark shadowed facets, and local
   texture/reflection variance the flat dotted background doesn't
   have), then keeping only the single large connected region so the
   background's polka-dot pattern -- which also has some local
   texture -- doesn't get caught up in it. Plain PNG alpha is
   universally supported, so layering this cutout on top and pulsing
   its own brightness/saturation/hue (same technique proven on the
   Interpreter Z card) confines the shimmer to the letters with no
   masking involved at all. --- */
.cv-shimmer {
    position: absolute;
    left: 27.47%;
    top: 13.9%;
    width: 47.34%;
    height: 77.44%;
    pointer-events: none;
    background-image: url(Images/grid/cv-letters.png);
    background-size: 100% 100%;
    animation: cv-shimmer-pulse 10s ease-in-out infinite;
}

@keyframes cv-shimmer-pulse {
    0%   { filter: brightness(1)   saturate(1)   hue-rotate(0deg);  opacity: 0.9; }
    30%  { filter: brightness(1.6) saturate(1.3) hue-rotate(8deg);  opacity: 1; }
    55%  { filter: brightness(1.1) saturate(1.1) hue-rotate(-4deg); opacity: 0.92; }
    78%  { filter: brightness(1.8) saturate(1.4) hue-rotate(6deg);  opacity: 1; }
    100% { filter: brightness(1)   saturate(1)   hue-rotate(0deg);  opacity: 0.9; }
}

@media (prefers-reduced-motion: reduce) {
    .cv-shimmer {
        animation: none;
        filter: brightness(1.2) saturate(1.15);
    }
}

/* --- Archives card: "Terminator eyes" on the chrome skull in
   Images/grid/archives.jpg -- eye socket positions measured by eye
   against the source photo. Each eye is a small glowing red dot plus
   a tall vertical beam (a scaleY'd bar spanning well past the card's
   own edges, clipped by archives-eyes' overflow:hidden) that stays
   hidden until the flash moment. One 2.5s loop: dim -> the eyes glow
   up red -> both beams snap on together for a bright vertical flash
   -> hold briefly -> fade back to dim and repeat, for that 80s-anime
   "eyes flash, then a scanning beam of light" reveal. --- */
.archives-eyes {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
}

.archives-eye {
    position: absolute;
    width: 6.2%;
    height: 5.1%;
    margin-left: -3.1%;
    margin-top: -2.55%;
    border-radius: 50%;
    background: radial-gradient(ellipse at center, #ffddc8 0%, #ff1a1a 28%, #8a0000 68%, transparent 85%);
    filter: drop-shadow(0 0 3px rgba(255, 20, 20, 0.95)) drop-shadow(0 0 8px rgba(200, 0, 0, 0.7));
    animation: archives-eye-glow 2.5s ease-in-out infinite;
}

.archives-eye-left { left: 56.76%; top: 48.24%; }
.archives-eye-right { left: 69.64%; top: 49.39%; animation-delay: 0.05s; }

.archives-eye::before {
    content: '';
    position: absolute;
    left: 50%;
    top: -9000%;
    width: 1px;
    height: 19000%;
    margin-left: -0.5px;
    background: linear-gradient(to bottom, transparent 0%, rgba(255, 60, 40, 0.95) 42%, #fff 50%, rgba(255, 60, 40, 0.95) 58%, transparent 100%);
    filter: drop-shadow(0 0 4px rgba(255, 40, 20, 0.9)) drop-shadow(0 0 12px rgba(255, 20, 20, 0.6));
    transform: scaleY(0);
    transform-origin: center;
    opacity: 0;
    animation: archives-beam-flash 2.5s ease-in-out infinite;
}

.archives-eye-right::before { animation-delay: 0.05s; }

@keyframes archives-eye-glow {
    0%   { opacity: 0.25; transform: scale(0.85); }
    20%  { opacity: 0.4;  transform: scale(0.9); }
    32%  { opacity: 0.75; transform: scale(1); }
    40%  { opacity: 1;    transform: scale(1.15); }
    50%  { opacity: 1;    transform: scale(1); }
    65%  { opacity: 0.7;  transform: scale(0.95); }
    85%  { opacity: 0.35; transform: scale(0.88); }
    100% { opacity: 0.25; transform: scale(0.85); }
}

@keyframes archives-beam-flash {
    0%, 34% { opacity: 0; transform: scaleY(0); }
    37%     { opacity: 1; transform: scaleY(1); }
    40%     { opacity: 1; transform: scaleY(1); }
    46%     { opacity: 0; transform: scaleY(1); }
    100%    { opacity: 0; transform: scaleY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .archives-eye {
        animation: none;
        opacity: 0.85;
    }
    .archives-eye::before {
        animation: none;
        opacity: 0;
    }
}

/* --- Academia card: day/night cycle across EVERY patch of sky
   visible in Images/grid/academia.jpg -- not just the strip above
   the beam, but every framed opening between the courtyard's
   columns too, since it's all the same real sky. Found all of it by
   scanning the whole photo for sky-blue pixels (not just the top),
   keeping every connected region above a noise-size threshold: the
   top strip plus about a dozen smaller column-gap patches. Rather
   than fight CSS masking again (proved unreliable on the Interpreter
   Z card), Images/grid/academia-night.png bakes the whole night sky
   -- dusk gradient AND a star field, positioned only within those
   exact sky pixels -- into one real RGBA cutout, generated from that
   same scan so it lines up with the photo exactly. Layering it with
   plain opacity keeps every patch in sync with no masking involved.

   The sun is a single div that moves continuously (real top/left/size
   animation, not a frame crossfade -- that looked choppy) starting
   from its original tuned ceiling: centered 29.05% down, sized so only
   ~20% of it pokes above the beam. Images/grid/academia-architecture.png
   is the *inverse* of the night-sky cutout -- the real photo's column/
   beam/wall pixels with the sky pixels punched transparent -- layered
   above the moving sun so it passes behind actual architecture exactly
   where the photo shows architecture, instead of needing to be masked
   itself. One slow 14s loop. --- */
.academia-sky {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

/* The card text sits over bright sky/pavement here instead of the dark
   photos most other cards have, so the plain gold fill loses contrast.
   A soft dark outline (stacked text-shadow, not -webkit-text-stroke --
   that draws a harder, less even edge) keeps it subtle but legible. */
a[href="/academia"] h3 {
    text-shadow:
        -1px -1px 1.5px rgba(0, 0, 0, 0.7),
         1px -1px 1.5px rgba(0, 0, 0, 0.7),
        -1px  1px 1.5px rgba(0, 0, 0, 0.7),
         1px  1px 1.5px rgba(0, 0, 0, 0.7),
         0    1px 3px   rgba(0, 0, 0, 0.5);
}

/* The subtitle's default color is --text-secondary, a pale gold at only
   55% opacity -- fine against the dark photos other cards use, but over
   bright sky/pavement the background bleeds straight through the letter
   fill, so an outline alone can't fix it. Bumped to a fully opaque warm
   tone, a solid black outline for edge definition, and a soft warm glow
   behind it so it reads as lit rather than just outlined. */
a[href="/academia"] p {
    color: #f3e4bf;
    text-shadow:
        -1px -1px 1.5px #000,
         1px -1px 1.5px #000,
        -1px  1px 1.5px #000,
         1px  1px 1.5px #000,
         0    0    2px  #000,
         0    1px  3px  #000,
         0    0    6px  rgba(255, 244, 214, 0.85),
         0    0    12px rgba(255, 224, 160, 0.55);
}

.academia-sun {
    position: absolute;
    top: 29.05%;
    left: 50%;
    width: 12.56%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle, #fff8dc 0%, #ffd76a 30%, #ffa733 62%, transparent 78%);
    filter: drop-shadow(0 0 6px rgba(255, 190, 80, 0.7)) hue-rotate(0deg) saturate(1) brightness(1);
    animation: academia-sun-motion 14s ease-in-out infinite;
}

.academia-architecture {
    position: absolute;
    inset: 0;
    background-image: url(Images/grid/academia-architecture.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
}

/* The beam shadows on the foreground walkway sway in sync with the sun's
   own descent -- a stylized second shadow layer (repeating diagonal
   stripes, mix-blend-mode:multiply so it only ever darkens, same trick
   already proven on Interpreter Z's glow) laid over the real photo's
   shadow band and skewed wider as the sun sinks, easing back upright at
   the ceiling. Confined to the plain concrete strip at the bottom of the
   photo where the real shadows read most clearly. */
.academia-shadows {
    position: absolute;
    left: 0;
    top: 82%;
    width: 100%;
    height: 18%;
    overflow: hidden;
    pointer-events: none;
    mix-blend-mode: multiply;
    opacity: 0.4;
}

.academia-shadows::before {
    content: '';
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: repeating-linear-gradient(
        112deg,
        rgba(20, 18, 15, 0.9) 0px,
        rgba(20, 18, 15, 0.9) 12px,
        transparent 12px,
        transparent 40px
    );
    transform-origin: center;
    animation: academia-shadow-sway 14s ease-in-out infinite;
}

@keyframes academia-shadow-sway {
    0%, 24%  { transform: skewX(0deg) rotate(0deg); }
    38%      { transform: skewX(-7deg) rotate(-3deg); }
    46%, 82% { transform: skewX(-13deg) rotate(-5deg); }
    90%      { transform: skewX(-7deg) rotate(-3deg); }
    100%     { transform: skewX(0deg) rotate(0deg); }
}

.academia-sky-night {
    position: absolute;
    inset: 0;
    background-image: url(Images/grid/academia-night.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    opacity: 0;
    animation: academia-daynight 14s ease-in-out infinite;
}

/* 3 stars that shimmer independently on top of the baked star field,
   riding inside .academia-sky-night so its own day/night opacity fade
   already hides them during the day for free -- no separate gating
   needed. Different durations/delays keep them out of sync with each
   other. */
.academia-star {
    position: absolute;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 4px 1px rgba(255, 255, 255, 0.9);
}
.academia-star-1 {
    top: 8%;
    left: 22%;
    animation: academia-star-shimmer 2.6s ease-in-out infinite;
}
.academia-star-2 {
    top: 15%;
    left: 61%;
    animation: academia-star-shimmer 3.4s ease-in-out infinite 0.8s;
}
.academia-star-3 {
    top: 5%;
    left: 84%;
    animation: academia-star-shimmer 2.1s ease-in-out infinite 1.4s;
}

@keyframes academia-star-shimmer {
    0%, 100% { opacity: 0.35; transform: scale(0.8); }
    50%      { opacity: 1;    transform: scale(1.3); }
}

@keyframes academia-daynight {
    0%, 32% { opacity: 0; }
    50%     { opacity: 1; }
    82%     { opacity: 1; }
    100%    { opacity: 0; }
}

@keyframes academia-sun-motion {
    0%, 24% {
        top: 29.05%; left: 50%; width: 12.56%; opacity: 1;
        filter: drop-shadow(0 0 6px rgba(255, 190, 80, 0.7)) hue-rotate(0deg) saturate(1) brightness(1);
    }
    38% {
        top: 40%; left: 48%; width: 10%; opacity: 1;
        filter: drop-shadow(0 0 6px rgba(255, 150, 60, 0.7)) hue-rotate(-12deg) saturate(1.15) brightness(0.95);
    }
    46% {
        top: 51%; left: 46%; width: 8%; opacity: 0.9;
        filter: drop-shadow(0 0 5px rgba(255, 110, 40, 0.6)) hue-rotate(-22deg) saturate(1.3) brightness(0.88);
    }
    50%, 82% {
        top: 58%; left: 46%; width: 6%; opacity: 0;
        filter: drop-shadow(0 0 4px rgba(230, 70, 20, 0.4)) hue-rotate(-30deg) saturate(1.4) brightness(0.8);
    }
    90% {
        top: 51%; left: 46%; width: 8%; opacity: 0.9;
        filter: drop-shadow(0 0 5px rgba(255, 110, 40, 0.6)) hue-rotate(-22deg) saturate(1.3) brightness(0.88);
    }
    96% {
        top: 40%; left: 48%; width: 10%; opacity: 1;
        filter: drop-shadow(0 0 6px rgba(255, 150, 60, 0.7)) hue-rotate(-12deg) saturate(1.15) brightness(0.95);
    }
    100% {
        top: 29.05%; left: 50%; width: 12.56%; opacity: 1;
        filter: drop-shadow(0 0 6px rgba(255, 190, 80, 0.7)) hue-rotate(0deg) saturate(1) brightness(1);
    }
}

@media (prefers-reduced-motion: reduce) {
    .academia-sky-night {
        animation: none;
        opacity: 0;
    }
    .academia-sun {
        animation: none;
        top: 29.05%;
        left: 50%;
        width: 12.56%;
        opacity: 1;
        filter: drop-shadow(0 0 6px rgba(255, 190, 80, 0.7));
    }
    .academia-shadows::before {
        animation: none;
        transform: skewX(0deg) rotate(0deg);
    }
    .academia-star {
        animation: none;
        opacity: 0.7;
    }
}

/* --- Author Spotlights card: color-pop on the one cyan pole in an
   otherwise black & white photo -- maxed out, this is a favorite author.
   Found the pole by scanning for cyan pixels (blue and green both well
   above red), excluding the image's own cyan border frame, then taking
   the two largest connected components -- the pole reads as two
   separate blobs because her hand wraps around it partway down, at
   y=44.52% of the photo. That gap is the whole concept: her hand is the
   power source, so every glow layer is weighted brightest there and
   fades toward the pole's two ends (a vertical Gaussian applied to the
   glow alpha at bake time, not just a uniform glow along the shaft),
   authors-hand-core.png adds a concentrated bright flare right at the
   grip, and the two energy sweeps both start at the hand and travel
   outward in opposite directions instead of one sweep running top to
   bottom.

   First pass glowed the whole shaft uniformly and it bled past the
   pole's actual tip into a soft round cloud -- read as an accidental
   floating orb instead of a controlled light source. Rebuilt the glow
   masks to dilate laterally only (a 1-row-tall structuring element, not
   a uniform blob dilation) and hard-clip vertically a couple px past
   the pole's real top/bottom, so the light hugs the shaft and caps
   cleanly instead of ballooning at the ends. Added authors-pole-
   hotcore.png, a thin near-white line straight down the pole's center,
   for the hot-core-plus-colored-bloom look real energy VFX use (a
   lightsaber blade, not a highlighter pen). Sparks are a thin cross of
   two bars, not round dots, on a sharp snap-on/snap-off timing curve
   instead of a smooth breathing fade -- reads as electric crackle
   rather than fairy-dust twinkle. Every color/glow piece is a real
   cutout cropped tight to its own bounding box (not the full canvas) to
   keep file size sane -- no masking involved. Rest of the photo stays
   grayscale via a plain CSS filter. The overlay stack carries the exact
   same transform as
   .grid-card img (including its hover zoom) so the color/glow stay
   pixel-aligned to the real pole instead of drifting as the photo
   scales; a hover-specific filter override keeps the grayscale-except-
   pole treatment from being undone by the site-wide hover rule that
   otherwise restores full color. The card text gets a scrim + outline
   since this photo has the author's own bio baked into it right where
   the title sits. */
a[href="/authors"] img {
    filter: grayscale(1) contrast(1.08);
}
a[href="/authors"]:hover img {
    filter: grayscale(1) contrast(1.08);
}

.authors-pole {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    transform: scale(1.04);
    transform-origin: center;
    transition: transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.grid-card:hover .authors-pole {
    transform: scale(1.1);
}

/* the photo's own decorative rounded-rect border is cyan too -- lit up
   the same way as the pole (real color cutout + a soft screen-blended
   glow) and pulsed on the exact same keyframe/timing as the pole's glow
   so it reads as one contained energy field around her rather than a
   second, competing light source. */
.authors-border-glow {
    position: absolute;
    inset: 0;
    background-image: url(Images/grid/authors-border-glow.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    mix-blend-mode: screen;
    animation: authors-glow-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.authors-border-color {
    position: absolute;
    inset: 0;
    background-image: url(Images/grid/authors-border-color.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    filter: saturate(1.3) brightness(1.05);
}

.authors-pole-glow-wide {
    position: absolute;
    left: 83.36%;
    top: 2.39%;
    width: 9.46%;
    height: 93.28%;
    background-image: url(Images/grid/authors-pole-glow-wide.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    mix-blend-mode: screen;
    animation: authors-glow-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.authors-pole-glow {
    position: absolute;
    left: 85.01%;
    top: 3.87%;
    width: 6.15%;
    height: 90.33%;
    background-image: url(Images/grid/authors-pole-glow.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    mix-blend-mode: screen;
    animation: authors-glow-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.authors-pole-hotcore {
    position: absolute;
    left: 87.09%;
    top: 4.97%;
    width: 2.07%;
    height: 88.12%;
    background-image: url(Images/grid/authors-pole-hotcore.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    mix-blend-mode: screen;
    animation: authors-core-flicker 3.4s ease-in-out infinite;
}

.authors-hand-core {
    position: absolute;
    left: 83.15%;
    top: 38.03%;
    width: 9.67%;
    height: 12.89%;
    background-image: url(Images/grid/authors-hand-core.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    mix-blend-mode: screen;
    animation: authors-core-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.authors-pole-color {
    position: absolute;
    left: 86.05%;
    top: 4.70%;
    width: 4.07%;
    height: 88.58%;
    background-image: url(Images/grid/authors-pole-color.png);
    background-size: 100% 100%;
    background-repeat: no-repeat;
    filter: saturate(1.45) contrast(1.1) brightness(1.05);
}

/* both sweeps share the pole's box and start at the hand's position
   within it -- (44.52 - 4.70) / 88.58 = ~45% down the box -- one
   travels up toward the tip, the other down toward the base. Tight and
   fast, like a discharge, not a slow gentle wave. */
.authors-pole-shimmer {
    position: absolute;
    left: 86.05%;
    top: 4.70%;
    width: 4.07%;
    height: 88.58%;
    overflow: hidden;
    mix-blend-mode: screen;
}

.authors-pole-shimmer::before {
    content: '';
    position: absolute;
    left: -60%;
    width: 220%;
    top: 45%;
    height: 16%;
    background: linear-gradient(180deg, transparent 0%, rgba(255, 255, 255, 1) 50%, transparent 100%);
    animation: authors-shimmer-up 1.9s cubic-bezier(0.25, 0, 0.3, 1) infinite;
}

.authors-pole-shimmer-2::before {
    height: 12%;
    background: linear-gradient(180deg, transparent 0%, rgba(160, 245, 255, 0.95) 50%, transparent 100%);
    animation: authors-shimmer-down 1.9s cubic-bezier(0.25, 0, 0.3, 1) infinite;
}

/* sparks read as electric crackle, not soft twinkle: a thin cross of
   two bars instead of a round dot, and a sharp snap-on/snap-off timing
   curve instead of a smooth breathing fade. */
.authors-spark {
    position: absolute;
    width: 9px;
    height: 9px;
    mix-blend-mode: screen;
    opacity: 0;
    animation: authors-spark-flare 1.6s cubic-bezier(0.1, 0, 0.2, 1) infinite;
}
.authors-spark::before,
.authors-spark::after {
    content: '';
    position: absolute;
    background: #eafeff;
    box-shadow: 0 0 5px 1px rgba(150, 240, 255, 1);
}
.authors-spark::before { top: 50%; left: 0; width: 100%; height: 1.5px; transform: translateY(-50%); }
.authors-spark::after  { left: 50%; top: 0; height: 100%; width: 1.5px; transform: translateX(-50%); }

.authors-spark-1 { left: 88.3%; top: 11%;    animation-duration: 1.5s; animation-delay: 0.2s; }
.authors-spark-2 {
    left: 87.9%; top: 44.5%; width: 13px; height: 13px;
    animation-duration: 1.3s; animation-delay: 0s;
}
.authors-spark-3 { left: 88.6%; top: 81%;    animation-duration: 1.4s; animation-delay: 0.7s; }

@keyframes authors-glow-pulse {
    0%   { opacity: 0.65; }
    18%  { opacity: 1;    }
    50%  { opacity: 0.8;  }
    100% { opacity: 0.65; }
}

@keyframes authors-core-flicker {
    0%, 100% { opacity: 0.92; }
    47%      { opacity: 1;    }
    50%      { opacity: 0.8;  }
    53%      { opacity: 1;    }
}

@keyframes authors-core-pulse {
    0%   { opacity: 0.75; transform: scale(1);    }
    18%  { opacity: 1;    transform: scale(1.15); }
    50%  { opacity: 0.85; transform: scale(1.03); }
    100% { opacity: 0.75; transform: scale(1);    }
}

@keyframes authors-shimmer-up {
    0%   { top: 45%;   opacity: 0; }
    10%  { opacity: 1; }
    100% { top: -20%;  opacity: 0; }
}

@keyframes authors-shimmer-down {
    0%   { top: 45%;   opacity: 0; }
    10%  { opacity: 1; }
    100% { top: 100%;  opacity: 0; }
}

@keyframes authors-spark-flare {
    0%, 78%, 100% { opacity: 0;   transform: scale(0.4) rotate(0deg);  }
    85%           { opacity: 1;   transform: scale(1.3) rotate(20deg); }
    92%           { opacity: 0.4; transform: scale(0.9) rotate(25deg); }
}

/* Bio text baked into the photo runs right up to where the card title
   sits, so this needs both a scrim (soft dark gradient behind the text
   block) and an outline -- an outline alone wasn't enough to separate
   two full lines of overlapping text. */
.authors-card-text {
    padding: 10px 12px 8px;
    border-radius: 6px;
    z-index: 0;
}
.authors-card-text::before {
    content: '';
    position: absolute;
    inset: -10px -12px -8px;
    background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.35) 35%, rgba(0, 0, 0, 0.72) 100%);
    border-radius: 8px;
    z-index: -1;
}
a[href="/authors"] h3 {
    text-shadow:
        -1px -1px 1.5px #000, 1px -1px 1.5px #000,
        -1px  1px 1.5px #000, 1px  1px 1.5px #000,
         0    1px 3px   #000;
}
a[href="/authors"] p {
    color: #eaf9fc;
    text-shadow:
        -1px -1px 1.5px #000, 1px -1px 1.5px #000,
        -1px  1px 1.5px #000, 1px  1px 1.5px #000,
         0    0    2px  #000, 0 1px 3px #000;
}

@media (prefers-reduced-motion: reduce) {
    .authors-border-glow {
        animation: none;
        opacity: 0.8;
    }
    .authors-pole-glow-wide {
        animation: none;
        opacity: 0.5;
        transform: scale(1);
    }
    .authors-pole-glow {
        animation: none;
        opacity: 0.8;
    }
    .authors-pole-hotcore {
        animation: none;
        opacity: 0.95;
    }
    .authors-hand-core {
        animation: none;
        opacity: 0.85;
    }
    .authors-pole-shimmer::before {
        animation: none;
        opacity: 0;
    }
    .authors-spark {
        animation: none;
        opacity: 0;
    }
    .authors-pole {
        transition: none;
    }
}

/* --- Essays & Audiolibros cards: same glowing-border language as
   Author Spotlights' cyan frame, each in its own accent color -- the
   Essays glyph's green (sampled straight from essays.png's icon,
   ~rgb(0,255,154)) and a cornflower blue for Audiolibros. Pure CSS
   (border + box-shadow) since neither photo has a colored border baked
   in the way authors.jpg does, so there's no pixel cutout to make.
   Stays fixed to the card's true edge rather than tracking the hover
   zoom transform -- unlike the Authors pole/border, this frame isn't
   locked to a feature inside the photo, so it reads better as a frame
   staying put while the photo moves behind it. Shares the Authors
   border's exact pulse timing so all three cards read as one
   consistent "energized frame" motif across the grid. */
.essays-border-glow {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border: 1.5px solid rgba(20, 255, 150, 0.9);
    box-shadow:
        0 0 8px 1px rgba(20, 255, 150, 0.8),
        0 0 20px 3px rgba(20, 255, 150, 0.45),
        inset 0 0 10px 1px rgba(20, 255, 150, 0.5);
    animation: authors-glow-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.audiolibros-border-glow {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border: 1.5px solid rgba(100, 149, 237, 0.9);
    box-shadow:
        0 0 8px 1px rgba(100, 149, 237, 0.8),
        0 0 20px 3px rgba(100, 149, 237, 0.45),
        inset 0 0 10px 1px rgba(100, 149, 237, 0.5);
    animation: authors-glow-pulse 2.2s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

@media (prefers-reduced-motion: reduce) {
    .essays-border-glow,
    .audiolibros-border-glow {
        animation: none;
        opacity: 0.85;
    }
}

/* --- El Cubo card: clouds drifting through the strip of real sky at
   the top of Images/grid/el-cubo.jpg. The photo's sky sits at y=0-18.7%
   but dips as low as 14.3% at the corners where the roofline slants
   into frame, so the band is clipped at a conservative 13% to guarantee
   it never draws over the architecture. The 3 wisps are painted
   procedurally (soft blurred blobs, not extracted from the photo) since
   a real photo's clouds can't tile/loop -- this way each one just drifts
   left to right on its own timer and fades in/out at the edges of its
   own travel, so there's no seam to hide. Different sizes/speeds/delays
   for a little parallax depth. mix-blend-mode:screen keeps them looking
   like part of the same bright sky instead of pasted-on shapes. */
.el-cubo-sky {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 13%;
    overflow: hidden;
    pointer-events: none;
}

.el-cubo-cloud {
    position: absolute;
    top: 0;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    mix-blend-mode: screen;
}

.el-cubo-cloud-1 {
    background-image: url(Images/grid/el-cubo-cloud-1.png);
    width: 34%;
    height: 60%;
    top: 8%;
    animation: el-cubo-drift 32s linear infinite;
}
.el-cubo-cloud-2 {
    background-image: url(Images/grid/el-cubo-cloud-2.png);
    width: 26%;
    height: 46%;
    top: 42%;
    opacity: 0.85;
    animation: el-cubo-drift 26s linear infinite;
    animation-delay: -9s;
}
.el-cubo-cloud-3 {
    background-image: url(Images/grid/el-cubo-cloud-3.png);
    width: 20%;
    height: 38%;
    top: 60%;
    opacity: 0.7;
    animation: el-cubo-drift 40s linear infinite;
    animation-delay: -22s;
}
.el-cubo-cloud-4 {
    background-image: url(Images/grid/el-cubo-cloud-4.png);
    width: 30%;
    height: 54%;
    top: 0%;
    opacity: 0.8;
    animation: el-cubo-drift 36s linear infinite;
    animation-delay: -15s;
}
.el-cubo-cloud-5 {
    background-image: url(Images/grid/el-cubo-cloud-5.png);
    width: 17%;
    height: 32%;
    top: 25%;
    opacity: 0.65;
    animation: el-cubo-drift 22s linear infinite;
    animation-delay: -5s;
}
.el-cubo-cloud-6 {
    background-image: url(Images/grid/el-cubo-cloud-2.png);
    width: 22%;
    height: 40%;
    top: 50%;
    opacity: 0.6;
    animation: el-cubo-drift 30s linear infinite;
    animation-delay: -27s;
}

@keyframes el-cubo-drift {
    0%   { left: -36%; opacity: 0; }
    8%   { opacity: 1; }
    92%  { opacity: 1; }
    100% { left: 112%;  opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .el-cubo-cloud {
        animation: none;
        opacity: 0;
    }
}

/* --- Footer --- */
.footer {
    padding: 60px 48px;
    border-top: 0.5px solid var(--border);
    text-align: center;
}

.footer-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    max-width: 800px;
    margin: 0 auto;
}

.footer-logo {
    width: 80px;
    height: 80px;
    object-fit: contain;
    filter: invert(1) sepia(1) saturate(1.5) hue-rotate(5deg) brightness(0.85);
}

.footer-title {
    font-family: var(--font-title);
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.2em;
    color: var(--gold);
}

.footer-tagline {
    font-family: var(--font-body);
    font-style: italic;
    font-size: 15px;
    color: var(--text-secondary);
}

.footer-links {
    font-family: var(--font-title);
    font-size: 11px;
    letter-spacing: 0.1em;
    color: var(--text-secondary);
}

.footer-links a {
    color: var(--text-secondary);
    text-decoration: none;
    transition: color 0.2s ease;
}

.footer-links a:hover {
    color: var(--gold);
}

.footer-copy {
    font-family: var(--font-body);
    font-size: 13px;
    color: rgba(232, 213, 163, 0.25);
}

.footer-bottom {
    font-family: var(--font-title);
    font-size: 11px;
    letter-spacing: 0.18em;
    color: var(--gold-dim);
    text-transform: uppercase;
}
/* --- Hamburger Menu --- */
.hamburger {
    display: none;
    background: none;
    border: none;
    color: var(--gold);
    font-size: 24px;
    cursor: pointer;
}

/* --- Hamburger menu for tablets in portrait ---
   The width-only breakpoint below misses tablets: an iPad in portrait
   is ~768-834px wide (wider than the 768px mobile cutoff), so it was
   still getting the full desktop nav crammed into the navbar. Switch
   to the hamburger any time the viewport is taller than it is wide
   (portrait) up through typical tablet widths, regardless of exact
   pixel width — landscape tablets keep the normal desktop nav. */
@media (orientation: portrait) and (max-width: 1024px) {
    .hamburger {
        display: block;
    }

    .nav-links {
        display: none;
        flex-direction: column;
        position: absolute;
        top: 65px;
        left: 0;
        width: 100%;
        background-color: rgba(10, 8, 5, 0.98);
        padding: 20px;
        gap: 20px;
        border-bottom: 0.5px solid var(--border);
        z-index: 99;
    }

    .nav-links.open {
        display: flex;
    }

    .navbar {
        position: relative;
    }
}

@media (max-width: 768px) {
    .hamburger {
        display: block;
    }

    .nav-links {
        display: none;
        flex-direction: column;
        position: absolute;
        top: 65px;
        left: 0;
        width: 100%;
        background-color: rgba(10, 8, 5, 0.98);
        padding: 20px;
        gap: 20px;
        border-bottom: 0.5px solid var(--border);
        z-index: 99;
    }

    .nav-links.open {
        display: flex;
    }

    .nav-links a {
        font-size: 14px;
    }

    .hero-title {
        font-size: 36px;
    }

    .hero-tagline {
        font-size: 12px;
    }

    .sections-grid {
        padding: 40px 20px;
    }

    .quotes {
        padding: 40px 20px;
    }

    .quotes-inner {
        grid-template-columns: 1fr;
    }

    .grid-inner {
        grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
        gap: 12px;
    }

    .footer {
        padding: 40px 20px;
    }

    .navbar {
        padding: 14px 20px;
        position: relative;
    }
}

.footer-logo-link {
    text-decoration: none;
    color: inherit;
    display: flex;
    flex-direction: column;
    align-items: center;
}
/* --- Gold Beams Backdrop (full page, fixed) --- */
#gold-beams-backdrop {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    background: #0a0805;
}

#gold-beams-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}