/* ==========================================================================
   Antalya Kebab Haus - Motion layer

   Everything that moves lives here, so components.css stays about looks.

   One rule governs this whole file: the "before" state of any reveal is put
   on the element BY JAVASCRIPT (the .rv class), never by static CSS. With
   scripts blocked or broken the page is therefore fully visible by default
   instead of a screen of invisible boxes.

   The second rule is the reduced-motion rescue at the bottom. base.css:24
   kills every animation- and transition-duration with !important, which
   would otherwise freeze a reveal in its hidden state forever. Anything
   that starts at opacity:0 or a zero-size clip-path must be rescued there.
   ========================================================================== */

:root {
  /* Pool of light that follows the pointer across the category grid. Green
     rather than the amber it started as, so it agrees with the green border
     and arrow the cards take on hover. Stronger on the obsidian ground,
     where there is more room to lift. */
  --oven-glow: rgba(76, 168, 61, 0.30);

  /* The two ends of the Y's pulse. Both must stay legible on their own —
     the letter is never allowed to fade towards the background, it only
     swings between a deep green and a lit one. */
  --y-dim: #2C6B24;
  --y-lit: #5FBF4C;
}

:root[data-theme="dark"] {
  --oven-glow: rgba(111, 212, 92, 0.30);
  --y-dim: #4CA83D;
  --y-lit: #93F582;
}


/* ==========================================================================
   Keyframes
   ========================================================================== */

/* The Y pulsing like the tube in the real shop sign. The colour swings as
   well as the glow — glow alone was too quiet to read as a pulse at logo
   size. Three seconds and ease-in-out: fast enough to be plainly visible,
   slow enough to be a hum rather than a strobe. */
@keyframes yPulse {
  0%, 100% {
    color: var(--y-dim);
    text-shadow: 0 2px 10px var(--color-green-glow);
  }
  50% {
    color: var(--y-lit);
    text-shadow: 0 0 7px var(--y-lit), 0 2px 28px var(--color-green-glow);
  }
}

/* Neon striking on: dark, two false starts, then it holds. The uneven
   percentages are the whole trick - evenly spaced steps read as a turn
   signal, uneven ones read as gas catching. */
/* Animates `color`, NOT -webkit-text-fill-color. The fill-color property
   beats `color` outright, and this animation ends in `forwards` — using it
   here would pin the letter to the ignite's final colour and yPulse would
   never show again. Both animations must drive the same property so the
   later one in the list can take over. */
@keyframes neonIgnite {
  0%   { color: var(--color-green-dark); opacity: 0.25; text-shadow: none; }
  6%   { color: var(--y-lit); opacity: 1; text-shadow: 0 0 22px var(--color-green-glow); }
  9%   { color: var(--color-green-dark); opacity: 0.25; text-shadow: none; }
  22%  { color: var(--color-green-dark); opacity: 0.25; text-shadow: none; }
  26%  { color: var(--y-lit); opacity: 1; text-shadow: 0 0 30px var(--color-green-glow); }
  31%  { color: var(--color-green-dark); opacity: 0.40; text-shadow: none; }
  38%  { color: var(--y-lit); opacity: 1; text-shadow: 0 0 34px var(--color-green-glow); }
  44%  { color: var(--y-dim); opacity: 0.70; text-shadow: 0 0 10px var(--color-green-glow); }
  100% { color: var(--y-dim); opacity: 1; text-shadow: 0 2px 10px var(--color-green-glow); }
}

/* A worn neon tube catching for a moment. Fired rarely and at random from
   js/motion.js, never on a fixed beat - a predictable flicker reads as an
   animation, an unpredictable one reads as a physical sign. */
@keyframes neonStutter {
  0%   { opacity: 1; }
  9%   { opacity: 0.32; }
  15%  { opacity: 1; }
  26%  { opacity: 0.45; }
  31%  { opacity: 1; }
  38%  { opacity: 0.7; }
  44%  { opacity: 1; }
  100% { opacity: 1; }
}

/* A glint, not a loading bar. The highlight has crossed the button by 18%
   of the cycle and then waits offscreen for the remaining 82%. */
@keyframes ctaSweep {
  0%   { transform: translateX(-140%) skewX(-20deg); }
  18%  { transform: translateX(320%) skewX(-20deg); }
  100% { transform: translateX(320%) skewX(-20deg); }
}

/* Dusk lifting off the storefront. The photo itself never moves; instead a
   cool evening wash over it clears, so the shop appears to switch its lights
   on - which is the same beat as the Y striking on in the header. Animating
   an overlay's opacity is also pure compositing, where scaling or filtering
   a full-bleed photo repaints it every frame. */
@keyframes duskLift {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* The live open badge takes the same visual language as the Y. */
@keyframes statusBreathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(76, 168, 61, 0); }
  50%      { box-shadow: 0 0 16px 0 var(--color-green-glow); }
}

/* The live dot on the hero ribbon. It needs its own keyframe rather than
   borrowing statusBreathe: that one was written for the green open/closed
   badge and animates the shadow to --color-green-glow, so the gold dot came
   out haloed in green on a ribbon that is deliberately amber. It also faded
   to fully transparent at both ends, leaving the dot flat half the time. */
@keyframes dotBreatheGold {
  0%, 100% { box-shadow: 0 0 6px 0 var(--color-gold-glow); }
  50%      { box-shadow: 0 0 16px 2px var(--color-gold-bright); }
}

/* Smooth downward fade-in for dynamic search / info results */
@keyframes slideDownFade {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Tactile button pop on click */
@keyframes pricePop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

/* Floating order notepad button bounce on adding an item */
@keyframes fabBump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.12) translateY(-4px); }
  70%  { transform: scale(0.96) translateY(0); }
  100% { transform: scale(1); }
}

/* Counter chip pop */
@keyframes countPop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}


/* ==========================================================================
   Reveal system  (states are applied by js/reveal.js)
   ========================================================================== */

.rv {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity 520ms var(--ease-out),
    transform 520ms var(--ease-out);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.rv.rv-in {
  opacity: 1;
  transform: none;
}

/* Food photography uncovers in a circle, like a cloche lifted off a plate.
   Applied to the image inside a revealing card rather than to the card
   itself, so the frame arrives first and the food fills it. */
.rv-plate img {
  clip-path: circle(0% at 50% 55%);
  transform: scale(1.06);
  transition:
    clip-path 720ms var(--ease-out),
    transform 720ms var(--ease-out);
  transition-delay: calc(var(--i, 0) * 70ms + 90ms);
}
.rv-plate.rv-in img {
  clip-path: circle(78% at 50% 55%);
  transform: none;
}

/* The cards carry their own hover transitions, which .rv would otherwise
   override — motion.css loads last, so at equal specificity it wins and the
   hover lift would inherit the slower reveal timing. Hand it back once the
   reveal has played. */
.rv.rv-in:hover {
  transition: all var(--dur-med) var(--ease-out);
}
.rv-plate.rv-in:hover img {
  transition: transform 0.6s var(--ease-out);
}


/* ==========================================================================
   Wordmark - the green Y is the only thing that moves

   The red letters and "Kebab Haus" are deliberately left completely still
   (their glow is a plain text-shadow in components.css). A sign reads as lit
   because something on it is alive against a steady background; animating
   the whole mark would flatten that back out. So the Y is the tube: it
   strikes on once per session, breathes, and now and then stutters the way
   a real one does.
   ========================================================================== */

.wordmark__y {
  animation: yPulse 3s ease-in-out infinite;
}

/* First visit of the session only - see js/motion.js. yPulse is listed
   second with a matching delay so it takes over the moment the tube settles;
   where two animations touch the same property the later one wins. */
.wordmark--ignite .wordmark__y {
  animation:
    neonIgnite 1.7s linear forwards,
    yPulse 3s ease-in-out 1.7s infinite;
}

/* The rare stutter, fired on a random timer. It only touches opacity, while
   yPulse drives colour and text-shadow, so the two run side by side without
   fighting over a property. */
.wordmark__y.is-stutter {
  animation:
    neonStutter 460ms linear,
    yPulse 3s ease-in-out infinite;
}

/* yPulse drives text-shadow, so a static hover glow here would simply lose to
   it — animations beat plain declarations. Quickening the pulse works with the
   animation instead of against it. The second rule keeps the ignite's own
   duration intact while still speeding up the pulse behind it. */
.wordmark:hover .wordmark__y {
  animation-duration: 1.1s;
}
.wordmark--ignite:hover .wordmark__y {
  animation-duration: 1.7s, 1.1s;
}


/* ==========================================================================
   Header - the .is-scrolled class was already being toggled by main.js:14
   with no styles attached to it. Several properties move together so the
   bar reads as settling rather than snapping.
   ========================================================================== */

.site-header {
  transition:
    background-color var(--dur-med) var(--ease-out),
    box-shadow var(--dur-med) var(--ease-out),
    border-color var(--dur-med) var(--ease-out);
}
.site-header__bar {
  transition: min-height var(--dur-med) var(--ease-out);
}
.site-header.is-scrolled {
  box-shadow: var(--shadow-lift);
  border-bottom-color: var(--color-gold);
}
.site-header.is-scrolled .site-header__bar {
  min-height: calc(var(--header-height) - 16px);
}
.wordmark__main,
.wordmark__sub {
  transition: font-size var(--dur-med) var(--ease-out);
}
.site-header.is-scrolled .wordmark__main { font-size: 1.6rem; }
@media (min-width: 860px) {
  .site-header.is-scrolled .wordmark__main { font-size: 2.05rem; }
}


/* ==========================================================================
   Scroll progress - paints the three brand colours as the page is read
   ========================================================================== */

.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  z-index: 700;
  pointer-events: none;
  background: linear-gradient(90deg, var(--color-red), var(--color-gold-bright), var(--color-green));
  will-change: width;
}


/* ==========================================================================
   Film grain - one static paint, no animation. Keeps the flat ivory ground
   from looking like plain #F4F0E8. CSP allows this: img-src 'self' data:.
   ========================================================================== */

.grain {
  position: fixed;
  inset: 0;
  z-index: 9000;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
:root[data-theme="dark"] .grain { opacity: 0.055; }


/* ==========================================================================
   Oven light - a warm pool follows the pointer across the category grid.

   One element for the whole grid rather than an ::after per card: the light
   then spills across the tile gaps, which is the entire effect, and it costs
   one transform per frame instead of eight rect measurements.
   ========================================================================== */

.cat-grid { position: relative; }

/* The light is 420px across and is centred on the pointer, so near the right
   edge of the grid it would hang ~200px past the viewport and give the whole
   document a horizontal scrollbar. This wrapper is pinned to the grid's own
   box and clips it. It sits over the cards as a separate layer, so their
   hover lift and shadow are untouched — and because the grid's edge is also
   the tiles' edge, the light stops exactly where the food stops. */
.oven-light-wrap {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 3;
  pointer-events: none;
}

.oven-light {
  position: absolute;
  top: 0;
  left: 0;
  width: 420px;
  height: 420px;
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(circle, var(--oven-glow) 0%, transparent 65%);
  filter: blur(24px);
  transition: opacity 450ms var(--ease-out);
  will-change: transform, opacity;
}
.cat-grid.is-lit .oven-light { opacity: 1; }

/* Pointer-driven, so it has no business existing on touch. */
@media (hover: none), (pointer: coarse) {
  .oven-light-wrap { display: none; }
}


/* ==========================================================================
   Call-to-action glint
   ========================================================================== */

.btn--primary::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 45%;
  height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.42), transparent);
  animation: ctaSweep 4.5s ease-in-out infinite;
  pointer-events: none;
}


/* ==========================================================================
   Hero - the old zoom hung off .hero:hover, so it never ran on a phone.
   Now it drifts on its own from first paint; js/motion.js adds the parallax.
   ========================================================================== */

/* The evening wash. It sits above the photo but below .hero__media::after,
   the permanent scrim — positioned pseudo-elements paint in order, and
   ::before comes first, so no z-index is needed. */
.hero__media::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(20, 30, 56, 0.52) 0%, rgba(10, 14, 30, 0.34) 100%);
  animation: duskLift 8s var(--ease-out) forwards;
}

/* The parallax shifts this box downwards, so it has to be taller than the
   hero or the page background appears above the photo. 14% of headroom
   against the 0.12 factor in js/motion.js, which tops out at 12%. */
.hero__media {
  top: -14%;
  height: 128%;
  will-change: transform;
}
.hero__content {
  will-change: transform, opacity;
}


/* ==========================================================================
   Live open/closed badge - the most useful sentence on the page, so it gets
   the same lit treatment as the Y. main.js writes this element's inline
   style, but it sets neither box-shadow nor animation, so these do not clash.
   ========================================================================== */

.open-status-badge.is-open {
  animation: statusBreathe 4s ease-in-out infinite;
}

/* Today's row in the opening-hours list, marked by main.js. */
.info-card dt.is-today,
.info-card dd.is-today {
  color: var(--color-green-ink);
  font-weight: 800;
}
.info-card dt.is-today::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  margin-right: 0.45rem;
  vertical-align: middle;
  background: var(--color-green);
  box-shadow: 0 0 8px var(--color-green-glow);
}
/* Tuesday is the rest day; it must not look like an invitation. */
.info-card dt.is-today.closed-day,
.info-card dd.is-today.closed-day {
  color: var(--color-red-ink);
}
.info-card dt.is-today.closed-day::before {
  background: var(--color-red);
  box-shadow: none;
}


/* ==========================================================================
   Order Pad / Price Button Micro-Interactions
   ========================================================================== */

.price-btn.is-added {
  animation: pricePop 350ms var(--ease-out);
}
.price-btn.is-added .price-btn__plus {
  transform: rotate(90deg) scale(1.15);
  transition: transform 300ms var(--ease-out), background 300ms var(--ease-out);
}

.notepad-fab.is-bumped {
  animation: fabBump 400ms var(--ease-out);
}
.notepad-fab.is-bumped .notepad-fab__count {
  animation: countPop 400ms var(--ease-out);
}


/* ==========================================================================
   Reduced motion

   base.css:24 already forces every duration to 0.01ms with !important. That
   alone would leave every .rv element stranded at opacity:0 and every plate
   image clipped to a zero-radius circle - invisible content, which is worse
   than the animation it was trying to spare. So each hidden start state is
   explicitly rescued here, and the looping ambient effects are switched off
   rather than merely sped up.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .rv,
  .rv.rv-in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .rv-plate img,
  .rv-plate.rv-in img {
    clip-path: none !important;
    transform: none !important;
    transition: none !important;
  }

  /* The Y stays lit, it just stops moving — parked at the bright end of the
     pulse rather than the dim one, so it still reads as switched on. */
  .wordmark__y,
  .wordmark--ignite .wordmark__y,
  .wordmark__y.is-stutter {
    animation: none !important;
    opacity: 1 !important;
    color: var(--y-lit);
    text-shadow: 0 0 7px var(--y-lit), 0 2px 20px var(--color-green-glow);
  }

  .btn--primary::after { display: none; }
  /* No dusk to lift — show the storefront as it is. */
  .hero__media::before { animation: none !important; opacity: 0 !important; }
  /* No parallax runs, so the extra headroom is not needed and would only
     crop the photo further. */
  .hero__media { top: 0; height: 100%; transform: none !important; }
  .hero__content { transform: none !important; opacity: 1 !important; }
  .open-status-badge.is-open { animation: none !important; }
  .oven-light-wrap { display: none !important; }
  .scroll-progress { display: none !important; }
  .hero__ribbon::before { animation: none !important; }
  .price-btn.is-added { animation: none !important; }
  .price-btn.is-added .price-btn__plus { transform: none !important; }
  .notepad-fab.is-bumped { animation: none !important; }
  .notepad-fab.is-bumped .notepad-fab__count { animation: none !important; }
}
