/* ============================================================
   MEDIA LAYER - shared across every Kobe site.
   TEMPLATE: copy to <site>/assets/media.css and load it AFTER
   site.css so the site's own tokens are already defined.

   Mobile first, in the literal sense: every block below is
   written for a 360px phone and only widens at min-width
   breakpoints. Phones are how these shops' customers actually
   arrive, usually standing outside the door.

   Requires from site.css: --bg --bg-2 --line --text --muted
   --accent --accent-ink --font-display --font-body
   ============================================================ */

/* ---------- Shared media primitives ---------- */

/* width/max-width/min-width are load-bearing, not tidiness. A grid item's
   default min-width is auto, which resolves to its min-content size, and an
   <img> carrying width="1600" reports 1600px of it. 16-the-city-kobe's .shots
   grid blew a 390px phone out to 980px the moment a real photograph replaced
   the drawn .m-ph, because a .m-ph has no intrinsic width and had always
   collapsed quietly. Any site putting a photo in a grid hits this. */
.m-figure { position: relative; overflow: hidden; background: var(--bg-2);
            width: 100%; max-width: 100%; min-width: 0; }
.m-figure img,
.m-figure video { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Reserve space before the image loads so nothing jumps (CLS). */
.m-ratio-1x1 { aspect-ratio: 1 / 1; }
.m-ratio-4x5 { aspect-ratio: 4 / 5; }
.m-ratio-9x16 { aspect-ratio: 9 / 16; }
.m-ratio-16x9 { aspect-ratio: 16 / 9; }

.m-cap {
  margin-top: 0.5rem;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--muted);
}

/* ---------- Photo placeholder ----------
   WHY THIS EXISTS. The demos originally used picsum.photos, which
   serves a RANDOM photograph per seed. Opening the pages in a real
   browser showed a yakitori shop with the Eiffel Tower as its hero
   and a charcoal izakaya fronted by a field of blue flowers. No
   code-level check could catch that; only looking could.

   A random irrelevant photo is worse than an honest gap: it makes
   the demo look careless to the one person we need to impress. So a
   missing photo is drawn, in the site's own palette, as a deliberate
   slot that names the shot it is waiting for. It costs no network
   request, cannot 404, never shifts layout, and doubles as a visible
   shot list during the pitch: everything on this page is your real
   information, and the photographs are the one thing we need from you.

   Usage:
     <div class="m-ph m-ratio-4x5" role="img" aria-label="店内の写真">
       <span class="m-ph-label">店内</span>
     </div>
   ---------- */

.m-ph {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  overflow: hidden;
  background-color: var(--bg-2);
  /* Depth from the site's own accent, kept low so it reads as a
     surface rather than as a coloured block. */
  background-image:
    radial-gradient(120% 90% at 22% 12%,
      color-mix(in srgb, var(--accent) 18%, transparent) 0%,
      transparent 60%),
    radial-gradient(90% 80% at 85% 90%,
      color-mix(in srgb, var(--accent) 10%, transparent) 0%,
      transparent 55%),
    repeating-linear-gradient(
      135deg,
      color-mix(in srgb, var(--text) 4%, transparent) 0 2px,
      transparent 2px 9px);
}

/* Browsers without color-mix still get a clean tinted surface. */
@supports not (background: color-mix(in srgb, red 10%, blue)) {
  .m-ph { background-image: repeating-linear-gradient(135deg, rgba(128,128,128,0.06) 0 2px, transparent 2px 9px); }
}

.m-ph::after {
  /* Hairline inset, so the slot reads as a designed frame. */
  content: "";
  position: absolute;
  inset: 0.5rem;
  border: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  pointer-events: none;
}

.m-ph-label {
  position: relative;
  z-index: 1;
  max-width: 82%;
  padding: 0.4rem 0.75rem;
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.06em;
  text-align: center;
  color: var(--muted);
}
.m-ph-label::before {
  content: "";
  display: block;
  width: 22px; height: 1px;
  margin: 0 auto 0.5rem;
  background: var(--accent);
}

/* ---------- Hero video ----------
   Autoplay only works muted + playsinline. The poster carries the
   first frame so the hero is never blank, and under reduced motion
   the video is hidden entirely and the poster stands alone. */

.m-hero-media { position: relative; width: 100%; height: 100%; }
.m-hero-media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.m-hero-media .m-poster {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.m-hero-media video + .m-poster { opacity: 0; transition: opacity 0.4s; }

@media (prefers-reduced-motion: reduce) {
  .m-hero-media video { display: none; }
  .m-hero-media .m-poster { opacity: 1 !important; }
}

/* A muted-video sound toggle must never be a hover-only control. */
.m-video-btn {
  position: absolute;
  right: 0.75rem; bottom: 0.75rem;
  z-index: 3;
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.35rem;
  padding: 0 0.8rem;
  font-family: var(--font-body);
  font-size: 0.8rem; font-weight: 700;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.35);
  cursor: pointer;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* ---------- Story strip ----------
   The vertical, tap-through format people already understand from
   phones. Horizontally scrollable on mobile with snap points. */

.m-stories {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  padding: 0.25rem clamp(1.25rem, 4vw, 3rem) 1rem;
  margin: 0 calc(-1 * clamp(1.25rem, 4vw, 3rem));
  scrollbar-width: none;
}
.m-stories::-webkit-scrollbar { display: none; }

/* .m-story works as a <button> (opens the viewer, when we hold the media)
   or as an <a> (goes to the shop's real Instagram highlight, when we do
   not). Both are 76px wide with a 72px ring, comfortably over the 44px
   touch minimum. */
.m-story {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: block;
  width: 76px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-body);
  color: inherit;
  text-align: center;
  text-decoration: none;
}
.m-story-ring {
  width: 72px; height: 72px;
  border-radius: 50%;
  padding: 3px;
  background: var(--accent);
  margin: 0 auto;
}
.m-story-ring img {
  width: 100%; height: 100%;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--bg);
  display: block;
}
/* Ring fallback when we do not hold the real highlight cover. A stock
   photo in a story ring would read as the shop's own content, so the
   ring carries an initial instead until the owner supplies covers. */
.m-story-ring .m-story-mono {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 2px solid var(--bg);
  background: var(--bg-2);
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
}

.m-story-label {
  display: block;
  margin-top: 0.45rem;
  font-size: 0.72rem;
  line-height: 1.3;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Story viewer (full screen) ---------- */

.m-viewer[hidden] { display: none !important; }
.m-viewer {
  position: fixed; inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  background: rgba(8, 8, 10, 0.94);
}
.m-viewer-stage {
  position: relative;
  width: 100%; height: 100%;
  max-width: 460px;
  display: flex; flex-direction: column;
}
.m-viewer-media {
  flex: 1;
  min-height: 0;
  display: grid;
  place-items: center;
  background: #000;
}
.m-viewer-media img,
.m-viewer-media video {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
}
.m-viewer-bars {
  display: flex; gap: 4px;
  padding: 0.6rem 0.75rem 0.4rem;
}
.m-viewer-bar {
  flex: 1; height: 3px;
  background: rgba(255, 255, 255, 0.3);
  overflow: hidden;
}
.m-viewer-bar i {
  display: block; height: 100%; width: 0;
  background: #fff;
}
.m-viewer-bar.is-done i { width: 100%; }
.m-viewer-cap {
  padding: 0.9rem 1rem 1.4rem;
  color: #fff;
  font-size: 0.9rem;
  line-height: 1.7;
  background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0));
}
/* Tap zones: left third goes back, right two thirds go forward. */
.m-viewer-prev, .m-viewer-next {
  position: absolute; top: 0; bottom: 0;
  z-index: 2;
  border: none; background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.m-viewer-prev { left: 0; width: 33%; }
.m-viewer-next { right: 0; width: 67%; }
.m-viewer-close {
  position: absolute; top: 0.5rem; right: 0.5rem;
  z-index: 4;
  min-width: 44px; min-height: 44px;
  font-size: 1.5rem; line-height: 1;
  color: #fff; background: none; border: none;
  cursor: pointer;
}

/* ---------- Gallery ---------- */

.m-gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}
.m-gallery button {
  padding: 0; border: none; background: none; cursor: pointer;
  display: block; width: 100%;
}
@media (min-width: 600px) {
  .m-gallery { grid-template-columns: repeat(2, 1fr); gap: 0.75rem; }
  /* Give the grid rhythm rather than a flat wall of equal tiles. */
  .m-gallery > *:nth-child(4n+1) { grid-column: span 2; }
}
@media (min-width: 960px) {
  .m-gallery { grid-template-columns: repeat(3, 1fr); }
  .m-gallery > *:nth-child(4n+1) { grid-column: span 2; grid-row: span 2; }
}

/* ---------- Instagram ----------
   The official embed is heavy (it pulls Instagram's own script), so
   the card renders as a real link with the shop's own photo and only
   upgrades to a live embed when the visitor asks for it. That keeps
   the page fast on a phone on mobile data, which is where these
   customers actually are. */

.m-ig {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}
@media (min-width: 600px) { .m-ig { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .m-ig { grid-template-columns: repeat(3, 1fr); } }

.m-ig-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  background: var(--bg-2);
  overflow: hidden;
}
.m-ig-card .m-cap { padding: 0 0.9rem; margin-top: 0.4rem; flex: 1; }

/* The card deliberately shows NO photograph before the embed is loaded.
   Instagram post images cannot be hotlinked, and dropping a stock
   placeholder into an Instagram card would read as the shop's own post
   when it is not. So the card states what the post is, and the real
   image arrives with the real embed on tap. */
.m-ig-kind {
  padding: 0.9rem 0.9rem 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--accent);
}
.m-ig-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 0.9rem 0.9rem;
}
.m-ig-actions .btn {
  min-height: 44px;
  padding: 0.5rem 1rem;
  font-size: 0.85rem;
}
.m-ig-open {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.25rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--muted);
  text-decoration: underline;
}
.m-ig-open:hover { color: var(--text); }

.m-ig-foot {
  display: flex; align-items: center; gap: 0.5rem;
  margin-top: 1rem;
  font-size: 0.9rem;
}
.m-ig-foot a {
  display: inline-flex; align-items: center;
  min-height: 44px;
  color: var(--accent);
  font-weight: 700;
  text-decoration: underline;
}

/* Instagram's own embed sets inline width; keep it inside the grid. */
.m-ig blockquote.instagram-media {
  width: 100% !important;
  min-width: 0 !important;
  margin: 0 !important;
}

/* ---------- Motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .m-stories { scroll-behavior: auto; }
  .m-viewer-bar i { transition: none !important; }
}

/* ============================================================
   MOTION + PHOTO LAYER
   Appended after the media primitives so it can lean on them.

   Everything here animates transform and opacity only, so it stays
   off the main thread. Every rule that moves anything sits inside
   prefers-reduced-motion: no-preference, which means "reduce" and
   browsers too old to understand the query both get the still page
   rather than a page that has to be un-animated afterwards.
   ============================================================ */

/* A div, not a figure, so there is no UA margin to reset; this is
   belt and braces in case a .m-figure is ever swapped for <figure>. */
.m-figure { margin: 0; }

/* ---------- Photo fade-in ----------
   Images arrive over the network at unpredictable moments, and an
   image that snaps in is more distracting than one that resolves.
   The image starts slightly transparent and settles once decoded.

   .is-loaded is set by JS. Without JS the img rule never applies at
   all (the selector needs the class), so the photograph is simply
   visible, which is the correct no-JS outcome. */
@media (prefers-reduced-motion: no-preference) {
  .m-fade img {
    opacity: 0;
    transform: scale(1.02);
    transition: opacity 0.7s ease, transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .m-fade img.is-loaded,
  .m-fade img[data-instant] { opacity: 1; transform: none; }
}

/* ---------- Staggered reveal ----------
   Siblings in a grid arriving in sequence reads as deliberate; the
   same siblings arriving together reads as a page that popped.

   --i is set by JS per child. The delay is capped by only setting
   --i on the first eight children, so a long list never leaves a
   reader waiting on arithmetic. */
@media (prefers-reduced-motion: no-preference) {
  .m-stagger > * { transition-delay: calc(var(--i, 0) * 70ms); }
}

/* ---------- Hero photograph ----------
   A very slow push-in. Twelve seconds and 3% is below the threshold
   where the eye reads it as movement; it reads as the image being
   alive. It runs once rather than looping, so it never becomes a
   distraction for someone reading the page. */
@media (prefers-reduced-motion: no-preference) {
  .hero-media .m-figure img,
  .hero .m-figure img {
    animation: m-drift 12s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
  }
}
@keyframes m-drift {
  from { transform: scale(1); }
  to   { transform: scale(1.03); }
}

/* ---------- Tile hover and focus ----------
   Focus is included deliberately: a keyboard user gets the same
   affordance a mouse user gets, and .m-gallery tiles can be buttons.
   Both states are transform and opacity only. */
@media (prefers-reduced-motion: no-preference) {
  .m-gallery .m-figure {
    transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .m-gallery .m-figure:hover,
  .m-gallery .m-figure:focus-within { transform: translateY(-4px); }
  .m-gallery .m-figure img { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
  .m-gallery .m-figure:hover img,
  .m-gallery .m-figure:focus-within img { transform: scale(1.04); }
}

/* Keyboard focus must stay visible even where hover does the lifting. */
.m-gallery .m-figure:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---------- Imagery disclosure ----------
   Quiet, but not hidden: this is the line that keeps a representative
   photograph from reading as the shop's own. Same size as the demo
   note it sits beside. */
.m-disclose {
  margin: 0 0 0.35rem;
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--muted);
}
.m-credit {
  margin: 0 0 0.35rem;
  font-size: 0.72rem;
  line-height: 1.7;
  color: var(--muted);
  opacity: 0.85;
}
.m-credit a { color: inherit; text-decoration: underline; }
.m-credit a:hover { color: var(--text); }

/* ---------- Even photo grid ----------
   Deliberately NOT .m-gallery. That grid promotes every 4n+1 tile to
   span two columns, which is a nice editorial rhythm for tiles that
   size themselves from their content, but these tiles carry a fixed
   aspect-ratio: doubling the width doubles the height too, and at
   768px the first tile measured 707x883 next to 347x195 neighbours.

   Square tiles keep every photograph the same weight at every width,
   and object-fit handles the portrait and landscape mix. ---------- */

.m-even { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; }
@media (min-width: 600px) { .m-even { grid-template-columns: repeat(3, 1fr); gap: 0.75rem; } }
@media (min-width: 960px) { .m-even { grid-template-columns: repeat(4, 1fr); gap: 1rem; } }

@media (prefers-reduced-motion: no-preference) {
  .m-even .m-figure { transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1); }
  .m-even .m-figure:hover,
  .m-even .m-figure:focus-within { transform: translateY(-4px); }
  .m-even .m-figure img { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
  .m-even .m-figure:hover img,
  .m-even .m-figure:focus-within img { transform: scale(1.04); }
}
.m-even .m-figure:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }
