/* ============================================================
   MEDIA LAYER - shared across every Kobe site.
   Copied from _template/media.css, the canonical source. Load 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 LAYER (appended to media.css, so it loads on every page
   without touching any <head>).

   Three rules govern everything below.

   1. Only transform and opacity are animated. Anything else makes
      the compositor lay out again on every frame, which on the
      cheap Android phones these customers actually carry reads as
      jank, not polish.
   2. Nothing here may be the only thing making content visible.
      Every stagger is scoped to .rv.in, so when the reveal script
      does not run, the <noscript> guard shows .rv and no child
      animation applies at all. Content first, motion second.
   3. All of it sits inside prefers-reduced-motion: no-preference.
      A reader who has asked their phone to stop moving things gets
      a still page, not a slower one.
   ============================================================ */

/* Photographs fade in as they decode. The DEFAULT IS VISIBLE and the fade is
   applied only to images this stylesheet's JS has taken charge of.

   The first version of this used `animation: fade 0.8s both`, which starts at
   opacity 0. Chrome throttles CSS animations in a tab that is not in front
   hard enough that the animation never advances, so every photograph on the
   page rendered as an empty box for anyone who opened the site in a background
   tab. An animation must never be the only thing making content visible. */
.m-photo { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .m-photo { transition: opacity 0.6s ease; }
  /* .m-js is set by media.js, so if scripting never runs this rule never
     applies and the photograph is simply visible. */
  html.m-js .m-photo:not(.is-loaded) { opacity: 0; }
}

/* ---------- Staggered reveal ----------
   Siblings in a grid arrive in sequence rather than as one block.
   Scoped to .rv.in: before the section is revealed these rules do
   not apply, so the children sit at their normal opacity and a
   scripting failure cannot hide them.

   The container list is deliberate rather than a blanket
   ".rv.in > *". An unscoped layout rule silently broke 04-eyelit's
   mobile collapse earlier in this project, and repeated-card grids
   are the only place a stagger reads as intentional. Forms
   (.reserve-grid) and dense text lists are left alone: nobody wants
   to watch their booking fields queue up. */
@keyframes m-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .rv.in .menu-grid    > *, .rv.in .veg-grid     > *,
  .rv.in .farms        > *, .rv.in .pillars      > *,
  .rv.in .rooms        > *, .rv.in .room-grid    > *,
  .rv.in .cast-grid    > *, .rv.in .bento        > *,
  .rv.in .courses      > *, .rv.in .course-grid  > *,
  .rv.in .cocktail-grid> *, .rv.in .mood-grid    > *,
  .rv.in .bottles-grid > *, .rv.in .price-grid   > *,
  .rv.in .story-grid   > *, .rv.in .gallery-strip> *,
  .rv.in .morning-grid > *, .rv.in .drinks       > *,
  .rv.in .m-gallery    > *,
  .m-stories.rv.in     > *, .m-ig.rv.in          > *,
  .rv.in .m-stories    > *, .rv.in .m-ig         > * {
    animation: m-rise 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  /* Capped at eight. Past that the last card would wait almost half
     a second, which stops reading as rhythm and starts reading as a
     slow page. */
  .rv.in .menu-grid    > *:nth-child(2), .rv.in .veg-grid     > *:nth-child(2),
  .rv.in .farms        > *:nth-child(2), .rv.in .pillars      > *:nth-child(2),
  .rv.in .rooms        > *:nth-child(2), .rv.in .room-grid    > *:nth-child(2),
  .rv.in .cast-grid    > *:nth-child(2), .rv.in .bento        > *:nth-child(2),
  .rv.in .courses      > *:nth-child(2), .rv.in .course-grid  > *:nth-child(2),
  .rv.in .cocktail-grid> *:nth-child(2), .rv.in .mood-grid    > *:nth-child(2),
  .rv.in .bottles-grid > *:nth-child(2), .rv.in .price-grid   > *:nth-child(2),
  .rv.in .story-grid   > *:nth-child(2), .rv.in .gallery-strip> *:nth-child(2),
  .rv.in .morning-grid > *:nth-child(2), .rv.in .drinks       > *:nth-child(2),
  .rv.in .m-gallery    > *:nth-child(2),
  .m-stories.rv.in     > *:nth-child(2), .m-ig.rv.in          > *:nth-child(2),
  .rv.in .m-stories    > *:nth-child(2), .rv.in .m-ig         > *:nth-child(2) { animation-delay: 0.07s; }

  .rv.in .menu-grid    > *:nth-child(3), .rv.in .veg-grid     > *:nth-child(3),
  .rv.in .farms        > *:nth-child(3), .rv.in .pillars      > *:nth-child(3),
  .rv.in .rooms        > *:nth-child(3), .rv.in .room-grid    > *:nth-child(3),
  .rv.in .cast-grid    > *:nth-child(3), .rv.in .bento        > *:nth-child(3),
  .rv.in .courses      > *:nth-child(3), .rv.in .course-grid  > *:nth-child(3),
  .rv.in .cocktail-grid> *:nth-child(3), .rv.in .mood-grid    > *:nth-child(3),
  .rv.in .bottles-grid > *:nth-child(3), .rv.in .price-grid   > *:nth-child(3),
  .rv.in .story-grid   > *:nth-child(3), .rv.in .gallery-strip> *:nth-child(3),
  .rv.in .morning-grid > *:nth-child(3), .rv.in .drinks       > *:nth-child(3),
  .rv.in .m-gallery    > *:nth-child(3),
  .m-stories.rv.in     > *:nth-child(3), .m-ig.rv.in          > *:nth-child(3),
  .rv.in .m-stories    > *:nth-child(3), .rv.in .m-ig         > *:nth-child(3) { animation-delay: 0.14s; }

  .rv.in .menu-grid    > *:nth-child(4), .rv.in .veg-grid     > *:nth-child(4),
  .rv.in .farms        > *:nth-child(4), .rv.in .pillars      > *:nth-child(4),
  .rv.in .rooms        > *:nth-child(4), .rv.in .room-grid    > *:nth-child(4),
  .rv.in .cast-grid    > *:nth-child(4), .rv.in .bento        > *:nth-child(4),
  .rv.in .courses      > *:nth-child(4), .rv.in .course-grid  > *:nth-child(4),
  .rv.in .cocktail-grid> *:nth-child(4), .rv.in .mood-grid    > *:nth-child(4),
  .rv.in .bottles-grid > *:nth-child(4), .rv.in .price-grid   > *:nth-child(4),
  .rv.in .story-grid   > *:nth-child(4), .rv.in .gallery-strip> *:nth-child(4),
  .rv.in .morning-grid > *:nth-child(4), .rv.in .drinks       > *:nth-child(4),
  .rv.in .m-gallery    > *:nth-child(4),
  .m-stories.rv.in     > *:nth-child(4), .m-ig.rv.in          > *:nth-child(4),
  .rv.in .m-stories    > *:nth-child(4), .rv.in .m-ig         > *:nth-child(4) { animation-delay: 0.21s; }

  .rv.in .menu-grid    > *:nth-child(n+5), .rv.in .veg-grid     > *:nth-child(n+5),
  .rv.in .farms        > *:nth-child(n+5), .rv.in .pillars      > *:nth-child(n+5),
  .rv.in .rooms        > *:nth-child(n+5), .rv.in .room-grid    > *:nth-child(n+5),
  .rv.in .cast-grid    > *:nth-child(n+5), .rv.in .bento        > *:nth-child(n+5),
  .rv.in .courses      > *:nth-child(n+5), .rv.in .course-grid  > *:nth-child(n+5),
  .rv.in .cocktail-grid> *:nth-child(n+5), .rv.in .mood-grid    > *:nth-child(n+5),
  .rv.in .bottles-grid > *:nth-child(n+5), .rv.in .price-grid   > *:nth-child(n+5),
  .rv.in .story-grid   > *:nth-child(n+5), .rv.in .gallery-strip> *:nth-child(n+5),
  .rv.in .morning-grid > *:nth-child(n+5), .rv.in .drinks       > *:nth-child(n+5),
  .rv.in .m-gallery    > *:nth-child(n+5),
  .m-stories.rv.in     > *:nth-child(n+5), .m-ig.rv.in          > *:nth-child(n+5),
  .rv.in .m-stories    > *:nth-child(n+5), .rv.in .m-ig         > *:nth-child(n+5) { animation-delay: 0.28s; }
}

/* ---------- Hero drift ----------
   A very slow settle from slightly scaled to true size. It is over
   inside ten seconds and never scales past the frame, so it cannot
   push the layout wider: the overflow: hidden below is what
   guarantees that, and is set here rather than assumed. */
@keyframes m-drift { from { transform: scale(1.06); } to { transform: scale(1); } }

.hero-media, .hero-photo, .hero-mosaic, .band, .room-photo,
.menu-feature, .morning-feature, .cell-photo { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  .hero-media > .m-photo,
  .hero-photo > .m-photo,
  .hero > .m-photo,
  .band > .m-photo {
    /* transform only: if this is throttled it simply holds at the start scale,
       which still shows the photograph. */
    animation: m-drift 9s cubic-bezier(0.2, 0.6, 0.2, 1) both;
  }
}

/* ---------- Tile hover and focus ----------
   Touch has no hover, so this is decoration for desktop and the
   focus half is the part that matters: it is how a keyboard reader
   knows where they are. Both states are given the same treatment so
   they can never drift apart. */
@media (prefers-reduced-motion: no-preference) {
  .m-gallery button, .m-story, .m-ig-card,
  .gallery-strip > *, .story-grid > * {
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
                box-shadow 0.35s ease;
  }
  .m-gallery button:hover, .m-story:hover, .m-ig-card:hover,
  .gallery-strip > *:hover, .story-grid > *:hover,
  .m-gallery button:focus-visible, .m-story:focus-visible, .m-ig-card:focus-within,
  .gallery-strip > *:focus-visible, .story-grid > *:focus-visible {
    transform: translateY(-3px);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
  }
  /* The photograph inside a tile leans in very slightly with it. */
  .m-gallery button:hover .m-photo,
  .gallery-strip > *:hover .m-photo { transform: scale(1.03); }
  .m-gallery button .m-photo,
  .gallery-strip > * .m-photo { transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
}
