---
// シリーズのページ（/series1〜8）。上に Studio で置いていた資料（Figma・スプレッドシートなど）、下にそのシリーズの記録
import Base from '../layouts/Base.astro';
import RecordCard from '../components/RecordCard.astro';
import LoadMore from '../components/LoadMore.astro';
import GuideBanner from '../components/GuideBanner.astro';
import ThinkingBanner from '../components/ThinkingBanner.astro';
import ServingBanner from '../components/ServingBanner.astro';
import { seriesCards, SERIES_PAGES } from '../lib/records';
import { media } from '../lib/media';
import staticPages from '../data/static.json';
import { SITE, SITE_NAME } from '../lib/seo';

export function getStaticPaths() {
  return Object.keys(SERIES_PAGES).map((series) => ({ params: { series } }));
}
const { series } = Astro.params;
const conf = SERIES_PAGES[series];
const blocks = (staticPages[series]?.blocks || []).slice(1); // 先頭はページの見出し
const items = await seriesCards(conf.series);
const [enName, jaName] = conf.title.split(' | ');
// 見出しは日本語を先に置く（検索結果のタイトルが見出しに置き換えられるため。2026-10-25）
const heading = jaName ? `${jaName} | ${enName}` : conf.title;
const description = conf.intro ? `${SITE_NAME} のシリーズ「${jaName || conf.title}」（${enName}）。${conf.intro}` : `${SITE_NAME} のシリーズ「${jaName || conf.title}」（${enName}）の記録一覧。ロボットとフィジカルAIを、29年のデザイナー経験から実機とシミュレーションで試した過程を、失敗も含めて公開しています。`;
const jsonLd = {
  '@context': 'https://schema.org', '@type': 'CollectionPage', name: heading, description, url: `${SITE}/${series}`,
  inLanguage: 'ja',
  hasPart: items.slice(0, 50).map((c) => ({ '@type': 'BlogPosting', headline: c.title, url: `${SITE}/record/${c.slug}` })),
};
---
<Base title={`${jaName || conf.title}（${enName}）| ${SITE_NAME}`} description={description} path={`/${series}`} jsonLd={jsonLd}>
  <section class="s-hero"><div class="panel">
    <h1 class="sec-h s-title">{heading}</h1>
    {conf.intro && <p class="s-intro">{conf.intro}</p>}
    {conf.points && (
      <div class="s-points">
        {conf.points.map((p) => (
          <div><h2 class="s-points-h">{p.h}</h2><ul class="s-obs-list">{p.items.map((t) => <li>{t}</li>)}</ul></div>
        ))}
      </div>
    )}
    {conf.guide && <div class="s-guide"><GuideBanner /></div>}
    {conf.thinking && <div class="s-guide"><ThinkingBanner /></div>}
    {conf.serving && <div class="s-guide"><ServingBanner /></div>}
    {blocks.map((b) => (
      b.type === 'text' ? (b.link ? <a class="s-label" href={b.link} target={b.link.startsWith('/') ? undefined : '_blank'} rel={b.link.startsWith('/') ? undefined : 'noopener'} set:html={b.html} /> : <p class="s-label" set:html={b.html} />)
      : b.type === 'iframe' ? <div class="s-frame" style={`height:${b.height}px`} set:html={b.html} />
      : b.type === 'embed' ? <div class="s-embed" set:html={b.html} />
      : b.type === 'img' ? (b.link ? <a class="s-img" href={b.link} target="_blank" rel="noopener"><img src={media(b.src)} alt={b.alt || conf.title} loading="lazy" /></a> : <div class="s-img"><img src={media(b.src)} alt={b.alt || conf.title} loading="lazy" /></div>)
      : null
    ))}
  </div></section>
  {conf.sim && (
    <section class="sec s-sim" data-ev-area="sim"><div class="panel">
      <h2 class="sec-h">試算を試す | Simulator</h2>
      {conf.sim.tabs && (
        <div class="s-tabs" role="tablist" aria-label="業種とロボットの仕事">
          {conf.sim.tabs.map((t, i) => (
            <button type="button" role="tab" class={`s-tab${t.img ? ' has-img' : ''}`} id={`simtab-${t.id}`} aria-selected={i === 0 ? 'true' : 'false'} aria-controls="sim-panel"
              data-tab={t.id} data-src={t.src} data-title={t.title || conf.sim.title} data-note={t.note || conf.sim.note} data-ev={`試算のタブ:${t.task}｜${t.place}`}>
              {t.img && <img src={t.img} alt="" loading="lazy" />}
              <b>{t.task}</b><span>{t.place.split('・').map((w, k, all) => <i>{w}{k < all.length - 1 ? '・' : ''}</i>)}</span>
            </button>
          ))}
        </div>
      )}
      <div id="sim-panel" role={conf.sim.tabs ? 'tabpanel' : undefined}>
        <p class="s-sim-title" id="sim-title">{conf.sim.title}</p>
        <p class="s-sim-note" id="sim-note">{conf.sim.note}</p>
        {conf.sim.obs && <a class="s-obs-jump" id="sim-obs-jump" href="#sim-obs" data-ev="観察シートへ移る">止まった理由・機種・場所を店で記録する観察シートは、試算の下にあります ↓</a>}
        <iframe data-embed id="sim-frame" src={`${conf.sim.src}?embed=1`} title={conf.sim.title} class="s-sim-frame" loading="lazy"></iframe>
        <a class="s-sim-open" id="sim-open" href={conf.sim.src} target="_blank" rel="noopener" data-ev="試算を全画面で開く">全画面で開く ↗</a>
      </div>
      {conf.sim.obs && (
        <div class="s-obs" id="sim-obs">
          <div class="s-obs-text">
            <h3 class="s-obs-h">{conf.sim.obs.title}</h3>
            <p class="s-sim-note">{conf.sim.obs.note}</p>
            <ul class="s-obs-list">{conf.sim.obs.points.map((t) => <li>{t}</li>)}</ul>
            <a class="s-obs-btn" href={conf.sim.obs.src} target="_blank" rel="noopener" data-ev="観察シートの例を開く">{conf.sim.obs.label}</a>
          </div>
          <div class="s-obs-phone">
            <iframe src={`${conf.sim.obs.src}?preview=1`} title={conf.sim.obs.title} loading="lazy"></iframe>
            <p class="s-obs-cap">スマホの幅で表示しています。中のボタンも押せます</p>
          </div>
        </div>
      )}
    </div></section>
  )}
  <script>
    // 試算のタブ：押すと中の試算を切り替える。/series8#sim-hospital のように開くと、そのタブから始まる
    const tabs = [...document.querySelectorAll<HTMLButtonElement>('.s-tab')];
    const frame = document.getElementById('sim-frame') as HTMLIFrameElement | null;
    if (tabs.length && frame) {
      const title = document.getElementById('sim-title')!, note = document.getElementById('sim-note')!;
      const open = document.getElementById('sim-open') as HTMLAnchorElement, obs = document.getElementById('sim-obs');
      const embedUrl = (src: string) => { const [path, hash] = src.split('#'); return `${path}?embed=1${hash ? '#' + hash : ''}`; };
      // 中の高さに合わせて枠の高さを変える（同じサイトのページなので中を読める）
      let ro: ResizeObserver | null = null;
      const fit = () => {
        try {
          const d = frame.contentDocument; if (!d || !d.body) return;
          // html の高さは枠の高さより小さくならないので、body の高さで測る
          const cs = getComputedStyle(d.body);
          frame.style.height = Math.ceil(d.body.getBoundingClientRect().height + parseFloat(cs.marginTop) + parseFloat(cs.marginBottom)) + 'px';
        } catch {}
      };
      frame.addEventListener('load', () => {
        fit(); ro?.disconnect();
        try { ro = new ResizeObserver(fit); ro.observe(frame.contentDocument!.body); } catch {}
      });
      const select = (t: HTMLButtonElement, push: boolean) => {
        tabs.forEach((b) => { const on = b === t; b.setAttribute('aria-selected', String(on)); b.tabIndex = on ? 0 : -1; });
        const src = t.dataset.src!;
        title.textContent = t.dataset.title!; note.textContent = t.dataset.note!;
        frame.title = t.dataset.title!; open.href = src;
        const next = embedUrl(src);
        const cur = frame.getAttribute('src') || '';
        if (cur !== next) {
          frame.setAttribute('src', next);
          // 同じページで # だけが違うとき、ブラウザによっては src を変えても中が切り替わらないので、中の # も直接変える
          const [np, nh] = next.split('#');
          if (cur.split('#')[0] === np && nh) { try { frame.contentWindow!.location.hash = nh; } catch {} }
        }
        if (obs) obs.hidden = t !== tabs[0];
        const jump = document.getElementById('sim-obs-jump'); if (jump) jump.hidden = t !== tabs[0];
        // スマホでは選んだタブが見える位置まで、タブの列を横に送る
        const strip = t.parentElement!; strip.scrollTo({ left: Math.max(t.offsetLeft - 8, 0), behavior: push ? 'smooth' : 'auto' });
        if (push) history.replaceState(null, '', t === tabs[0] ? location.pathname : `#sim-${t.dataset.tab}`);
      };
      tabs.forEach((t, i) => {
        t.tabIndex = i === 0 ? 0 : -1;
        t.addEventListener('click', () => select(t, true));
        t.addEventListener('keydown', (e) => {
          const k = e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : 0;
          if (!k) return;
          e.preventDefault();
          const n = tabs[(i + k + tabs.length) % tabs.length]; n.focus(); select(n, true);
        });
      });
      const start = tabs.find((t) => location.hash === `#sim-${t.dataset.tab}`);
      if (start) { select(start, false); document.querySelector('.s-sim')?.scrollIntoView(); }
    }
  </script>
  <section class="sec"><div class="panel">
    <h2 class="sec-h">記録 | Record</h2>
    <LoadMore limit={conf.limit} total={items.length}>{items.map((c) => <RecordCard item={c} />)}</LoadMore>
  </div></section>
</Base>
<style>
  .s-hero { padding: 88px 80px 0; }
  .s-title { margin: 0; }
  .s-guide { margin: 24px 0 0; }
  .s-intro { margin: 16px 0 0; font: 400 16px/28px var(--sans); color: var(--ink-2); }
  .s-points { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; margin: 24px 0 0; }
  .s-points-h { margin: 0 0 8px; font: 600 16px/26px var(--sans); color: var(--ink); }
  .s-points .s-obs-list { margin: 0; }
  @media (max-width: 767px) { .s-points { grid-template-columns: minmax(0, 1fr); gap: 20px; } }
  .s-label { display: block; margin: 40px 0 16px; font: 500 16px/24px var(--sans); color: var(--ink); }
  .s-title + .s-label { margin-top: 8px; }
  a.s-label:hover { text-decoration: underline; }
  .s-frame { width: 100%; }
  .s-frame :global(iframe) { display: block; width: 100% !important; height: 100% !important; }
  .s-embed :global(.embed iframe) { display: block; width: 100%; height: 150px; border: 0; }
  .s-img img { width: 100%; height: auto; display: block; }
  .s-sim-title { margin: -12px 0 8px; font: 600 18px/28px var(--sans); color: var(--ink); }
  .s-sim-note { margin: 0 0 20px; font: 400 14px/24px var(--sans); color: var(--ink-2); }
  .s-sim-frame { display: block; width: 100%; height: 1600px; border: 0; }
  .s-tabs { position: relative; display: flex; gap: 8px; margin: -8px 0 28px; overflow-x: auto; padding: 8px; background: #111; border-radius: 12px; scrollbar-width: thin; }
  .s-tab { flex: 1 0 140px; display: grid; gap: 2px; align-content: start; text-align: left; padding: 12px 14px; border: 0; border-radius: 8px; background: #2a2a2a; color: #f5f5f0; cursor: pointer; font: inherit; }
  .s-tab img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 4px; margin-bottom: 8px; opacity: .78; transition: opacity .2s; }
  .s-tab:hover img, .s-tab[aria-selected="true"] img { opacity: 1; }
  .s-tab.has-img { padding: 8px 8px 12px; }
  .s-tab b { font: 600 16px/24px var(--sans); }
  .s-tab span { font: 400 12px/18px var(--sans); color: #b8b8b0; }
  .s-tab span i { font-style: normal; display: inline-block; } /* 「商業施設・ビル」は、折り返すなら・のあとで折り返す */
  .s-tab:hover { background: #3a3a3a; }
  .s-tab[aria-selected="true"] { background: #f5f5f0; color: #111; }
  .s-tab[aria-selected="true"] span { color: #555; }
  .s-tab:focus-visible { outline: 2px solid #f5f5f0; outline-offset: 2px; }
  /* スマホでは横スクロールにせず、2列に並べて6つとも見せる */
  @media (max-width: 599px) {
    .s-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); overflow: visible; gap: 6px; padding: 6px; }
    /* 写真を左に、文字を右に置いて、カードの高さを抑える。カードの余白はなくし、写真をカードの端まで広げる */
    .s-tab, .s-tab.has-img { grid-template-columns: 72px minmax(0, 1fr); grid-template-rows: 1fr 1fr; align-content: stretch; column-gap: 8px; row-gap: 0; align-items: center; padding: 0; overflow: hidden; min-height: 58px; }
    .s-tab img { grid-row: 1 / span 2; align-self: stretch; width: 100%; height: 100%; aspect-ratio: auto; margin: 0; border-radius: 0; }
    .s-tab b { font-size: 15px; line-height: 20px; align-self: end; padding: 8px 4px 0 0; }
    .s-tab span { font-size: 11px; line-height: 16px; align-self: start; padding: 0 4px 8px 0; }
  }
  .s-sim-open { display: inline-block; margin-top: 16px; font: 500 14px/22px var(--sans); color: var(--ink); }
  .s-sim-open:hover { text-decoration: underline; }
  .s-obs { margin-top: 40px; padding-top: 40px; border-top: 1px solid var(--line, #e6e6e6); display: grid; grid-template-columns: minmax(0, 1fr) 390px; gap: 48px; align-items: start; }
  .s-obs[hidden], .s-obs-jump[hidden] { display: none; }
  .s-obs-jump { display: inline-block; margin: -8px 0 20px; font: 500 14px/22px var(--sans); color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
  .s-obs { scroll-margin-top: 96px; }
  .s-obs-h { margin: 0 0 8px; font: 600 18px/28px var(--sans); color: var(--ink); }
  .s-obs .s-sim-note { margin: 0 0 16px; }
  .s-obs-list { margin: 0 0 24px; padding-left: 1.2em; font: 400 14px/26px var(--sans); color: var(--ink-2); }
  .s-obs-btn { display: inline-flex; align-items: center; gap: 6px; padding: 12px 20px; border-radius: 8px; background: var(--ink, #242424); color: #fff; font: 600 14px/20px var(--sans); }
  .s-obs-btn:hover { opacity: .85; }
  .s-obs-phone iframe { display: block; width: 390px; max-width: 100%; height: 720px; border: 10px solid #242424; border-radius: 32px; background: #f7f7f7; }
  .s-obs-cap { margin: 8px 0 0; text-align: center; font: 400 12px/18px var(--sans); color: var(--ink-2); }
  @media (max-width: 1023px) { .s-obs { grid-template-columns: minmax(0, 1fr); gap: 24px; } .s-obs-phone { justify-self: center; width: 100%; max-width: 390px; } .s-obs-phone iframe { height: 640px; } }
  @media (max-width: 1023px) { .s-hero { padding: 72px 24px 0; } }
  @media (max-width: 599px) { .s-hero { padding: 64px 12px 0; } }
</style>
