const { useMemo, useRef, useState } = React;

const INITIAL_CITIES = [
  { id: 1, country: "페루", name: "리마", elev: 150, gps: "-12.0464, -77.0428", on: true },
  { id: 2, country: "페루", name: "와카치나", elev: 400, gps: "-14.0875, -75.7626", on: true },
  { id: 3, country: "페루", name: "파라카스", elev: 10, gps: "-13.8369, -76.2497", on: true },
  { id: 4, country: "페루", name: "쿠스코", elev: 3400, gps: "-13.5319, -71.9675", on: true },
  { id: 5, country: "페루", name: "우루밤바", elev: 2870, gps: "-13.3047, -72.1158", on: true },
  { id: 6, country: "페루", name: "오얀따이땀보", elev: 2792, gps: "-13.2583, -72.2642", on: true },
  { id: 7, country: "페루", name: "아구아스 칼리엔테스", elev: 2040, gps: "-13.1547, -72.5258", on: true },
  { id: 8, country: "페루", name: "마추픽추", elev: 2430, gps: "-13.1631, -72.5450", on: true },
  { id: 9, country: "페루", name: "성스러운 계곡", elev: 2800, gps: "-13.3150, -72.1167", on: false },
  { id: 10, country: "페루", name: "비니쿤카 (무지개산)", elev: 5200, gps: "-13.8700, -71.3020", on: true },
  { id: 11, country: "볼리비아", name: "라파즈", elev: 3640, gps: "-16.4897, -68.1193", on: true },
  { id: 12, country: "볼리비아 / 페루", name: "티티카카호", elev: 3812, gps: "-15.9254, -69.3354", on: true },
  { id: 13, country: "볼리비아", name: "우유니", elev: 3656, gps: "-20.4606, -66.8250", on: true },
  { id: 14, country: "볼리비아", name: "오야게 (Ollague)", elev: 3705, gps: "-21.2275, -68.2531", on: false },
  { id: 15, country: "볼리비아", name: "알티플라노 고원지대", elev: 4000, gps: "-17.0000, -68.5000", on: false },
  { id: 16, country: "칠레", name: "산 페드로 데 아타카마", elev: 2400, gps: "-22.9087, -68.1997", on: true },
  { id: 17, country: "칠레", name: "엘 타티오 간헐천", elev: 4300, gps: "-22.3344, -68.0119", on: true },
  { id: 18, country: "칠레", name: "산티아고", elev: 520, gps: "-33.4489, -70.6693", on: true },
  { id: 19, country: "칠레", name: "푸에르토 나탈레스", elev: 10, gps: "-51.7236, -72.4875", on: false },
  { id: 20, country: "칠레", name: "토레스 델 파이네 국립공원", elev: 110, gps: "-51.0413, -73.0698", on: true },
  { id: 21, country: "아르헨티나", name: "엘 칼라파테", elev: 187, gps: "-50.3379, -72.2648", on: true },
  { id: 22, country: "아르헨티나", name: "엘 찰텐", elev: 397, gps: "-49.3315, -72.8863", on: true },
  { id: 23, country: "아르헨티나", name: "우수아이아", elev: 23, gps: "-54.8019, -68.3030", on: true },
  { id: 24, country: "아르헨티나", name: "부에노스 아이레스", elev: 25, gps: "-34.6037, -58.3816", on: true },
  { id: 25, country: "우루과이", name: "콜로니아 델 사크라멘토", elev: 27, gps: "-34.4711, -57.8442", on: true },
  { id: 26, country: "브라질 / 아르헨티나", name: "이과수 폭포", elev: 195, gps: "-25.6953, -54.4367", on: true },
  { id: 27, country: "브라질", name: "리우데자네이루", elev: 5, gps: "-22.9068, -43.1729", on: true }
];

const TOGGLE_META = [
  ["yAxis", "Y축", "고도 축 표시"],
  ["xAxis", "기준선", "하단 기준선 표시"],
  ["grid", "그리드", "고도 눈금 가이드"],
  ["droplines", "드롭라인", "도시별 세로 연결선"],
  ["fill", "면 채우기", "고도 면적 강조"],
  ["order", "순서 번호", "여행 순서 표시"],
  ["cityLabels", "도시 라벨", "도시 이름 표시"],
  ["elev", "고도 수치", "도시별 고도 표시"],
  ["countryBars", "국가 바", "국가별 구간 묶음"]
];

function smoothPath(points, tension = 0.55) {
  if (points.length < 2) return "";
  if (points.length === 2) {
    return `M ${points[0].x} ${points[0].y} L ${points[1].x} ${points[1].y}`;
  }

  let d = `M ${points[0].x} ${points[0].y}`;
  for (let i = 0; i < points.length - 1; i += 1) {
    const p0 = points[i - 1] || points[i];
    const p1 = points[i];
    const p2 = points[i + 1];
    const p3 = points[i + 2] || p2;
    const c1x = p1.x + (p2.x - p0.x) * (tension / 6);
    const c1y = p1.y + (p2.y - p0.y) * (tension / 6);
    const c2x = p2.x - (p3.x - p1.x) * (tension / 6);
    const c2y = p2.y - (p3.y - p1.y) * (tension / 6);
    d += ` C ${c1x.toFixed(2)} ${c1y.toFixed(2)}, ${c2x.toFixed(2)} ${c2y.toFixed(2)}, ${p2.x.toFixed(2)} ${p2.y.toFixed(2)}`;
  }
  return d;
}

function niceMax(maxElev) {
  if (maxElev <= 1000) return 1000;
  if (maxElev <= 3000) return Math.ceil(maxElev / 250) * 250;
  return Math.ceil(maxElev / 500) * 500;
}

function downloadBlob(dataUrl, fileName) {
  const anchor = document.createElement("a");
  anchor.href = dataUrl;
  anchor.download = fileName;
  document.body.appendChild(anchor);
  anchor.click();
  document.body.removeChild(anchor);
}

function Chart({ cities, toggles, tension, labelOffset, labelSize, svgRef }) {
  const visible = cities.filter((city) => city.on);
  const W = 1440;
  const H = 760;
  const M = { top: 74, right: 70, bottom: 130, left: 86 };
  const innerW = W - M.left - M.right;
  const innerH = H - M.top - M.bottom;

  const maxElev = visible.length ? Math.max(...visible.map((city) => city.elev)) : 1000;
  const yMax = niceMax(maxElev + 300);
  const baseY = M.top + innerH;

  const yScale = (elev) => M.top + innerH - (elev / yMax) * innerH;
  const xScale = (index) => {
    if (visible.length <= 1) return M.left + innerW / 2;
    return M.left + (index / (visible.length - 1)) * innerW;
  };

  const points = visible.map((city, index) => ({
    x: xScale(index),
    y: yScale(city.elev),
    city
  }));

  const path = smoothPath(points, tension);
  const fillPath = points.length > 1
    ? `${path} L ${points[points.length - 1].x} ${baseY} L ${points[0].x} ${baseY} Z`
    : "";

  const tickCount = Math.min(10, Math.max(4, Math.round(yMax / 600)));
  const step = yMax / tickCount;
  const yTicks = Array.from({ length: tickCount + 1 }, (_, index) => index * step);

  const groups = [];
  visible.forEach((city, index) => {
    const last = groups[groups.length - 1];
    if (last && last.country === city.country) {
      last.end = index;
      last.cities.push(city);
    } else {
      groups.push({ country: city.country, start: index, end: index, cities: [city] });
    }
  });

  return (
    <svg
      ref={svgRef}
      viewBox={`0 0 ${W} ${H}`}
      preserveAspectRatio="xMidYMid meet"
      xmlns="http://www.w3.org/2000/svg"
      role="img"
      aria-label="남미 여행 루트 고도 차트"
    >
      <defs>
        <linearGradient id="routeFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#1c7c54" stopOpacity="0.26" />
          <stop offset="52%" stopColor="#1c7c54" stopOpacity="0.1" />
          <stop offset="100%" stopColor="#1c7c54" stopOpacity="0" />
        </linearGradient>
      </defs>

      {toggles.grid && yTicks.map((value, index) => (
        <line
          key={`grid-${index}`}
          className="grid-line"
          x1={M.left}
          x2={M.left + innerW}
          y1={yScale(value)}
          y2={yScale(value)}
        />
      ))}

      {toggles.yAxis && yTicks.map((value, index) => (
        <text
          key={`tick-${index}`}
          className="axis-tick"
          x={M.left - 12}
          y={yScale(value)}
          textAnchor="end"
          dominantBaseline="central"
        >
          {Math.round(value).toLocaleString()}m
        </text>
      ))}

      {toggles.xAxis && (
        <line className="axis-line" x1={M.left - 8} x2={M.left + innerW + 8} y1={baseY} y2={baseY} />
      )}

      {toggles.droplines && points.map((point, index) => (
        <line
          key={`drop-${index}`}
          className="drop-line"
          x1={point.x}
          x2={point.x}
          y1={point.y + 8}
          y2={baseY - 10}
        />
      ))}

      {toggles.fill && points.length > 1 && <path className="route-fill" d={fillPath} />}
      {points.length > 1 && <path className="route-line" d={path} />}

      {points.map((point) => (
        <g key={`point-${point.city.id}`}>
          <circle className="city-dot" cx={point.x} cy={point.y} r="4.5" />
          {toggles.cityLabels && (
            <CityLabel
              x={point.x}
              y={point.y}
              city={point.city}
              elevShow={toggles.elev}
              offset={labelOffset}
              size={labelSize}
            />
          )}
        </g>
      ))}

      {toggles.order && points.map((point, index) => (
        <g key={`order-${point.city.id}`} transform={`translate(${point.x}, ${baseY + 26})`}>
          <circle className="order-circle" r="11.5" />
          <text className="order-num" y="0.5">{index + 1}</text>
        </g>
      ))}

      {toggles.countryBars && groups.map((group, index) => {
        const x1 = xScale(group.start);
        const x2 = xScale(group.end);
        const centerX = (x1 + x2) / 2;
        const y = baseY + 64;
        return (
          <g key={`group-${index}`}>
            <line className="country-bar" x1={x1 - 10} x2={x2 + 10} y1={y} y2={y} />
            <line className="country-bracket" x1={x1 - 10} x2={x1 - 10} y1={y - 5} y2={y + 5} />
            <line className="country-bracket" x1={x2 + 10} x2={x2 + 10} y1={y - 5} y2={y + 5} />
            <text className="country-label" x={centerX} y={y + 22}>{group.country}</text>
          </g>
        );
      })}
    </svg>
  );
}

function CityLabel({ x, y, city, elevShow, offset, size }) {
  const dy = -22 - offset;
  const elevSize = Math.max(10, size - 2);
  return (
    <g>
      <text
        x={x}
        y={y + dy}
        textAnchor="middle"
        style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: `${size}px`, fill: "var(--ink)" }}
      >
        {city.name}
      </text>
      {elevShow && (
        <text
          x={x}
          y={y + dy + size + 7}
          textAnchor="middle"
          style={{ fontFamily: "var(--mono)", fontWeight: 600, fontSize: `${elevSize}px`, fill: "var(--ink-2)" }}
        >
          {city.elev.toLocaleString()}m
        </text>
      )}
    </g>
  );
}

function ToggleChip({ active, label, hint, onClick }) {
  return (
    <button type="button" className={`toggle-chip ${active ? "on" : ""}`} onClick={onClick} title={hint}>
      <span>{label}</span>
      <span className="toggle-indicator">{active ? "ON" : "OFF"}</span>
    </button>
  );
}

function CityRow({
  city,
  idx,
  isDragging,
  isDropTarget,
  onToggle,
  onChange,
  onDelete,
  onDragStart,
  onDragOver,
  onDrop
}) {
  return (
    <div
      className={`city-row ${city.on ? "" : "dim"} ${isDragging ? "dragging" : ""} ${isDropTarget ? "drop-target" : ""}`}
      draggable
      onDragStart={(event) => onDragStart(idx, event)}
      onDragOver={(event) => onDragOver(idx, event)}
      onDrop={(event) => onDrop(idx, event)}
      onDragEnd={() => onDragOver(-1)}
    >
      <div className="drag-handle" title="드래그해서 순서를 바꾸세요">⋮⋮</div>
      <input
        type="checkbox"
        className="city-check"
        checked={city.on}
        onChange={() => onToggle(city.id)}
        aria-label={`${city.name} 표시 여부`}
      />
      <div className="city-fields">
        <div className="field-group">
          <label>도시</label>
          <input
            type="text"
            value={city.name}
            onChange={(event) => onChange(city.id, "name", event.target.value)}
            placeholder="도시명"
          />
        </div>
        <div className="field-group">
          <label>고도</label>
          <input
            type="number"
            className="elev-input"
            value={city.elev}
            onChange={(event) => onChange(city.id, "elev", Number(event.target.value) || 0)}
            placeholder="0"
          />
        </div>
        <div className="field-group country-row">
          <label>국가</label>
          <input
            type="text"
            value={city.country}
            onChange={(event) => onChange(city.id, "country", event.target.value)}
            placeholder="국가명"
          />
        </div>
        <div className="gps-pill">{city.gps}</div>
      </div>
      <button type="button" className="delete-btn" onClick={() => onDelete(city.id)} title="도시 삭제">×</button>
    </div>
  );
}

function App() {
  const [cities, setCities] = useState(INITIAL_CITIES);
  const [toggles, setToggles] = useState({
    yAxis: true,
    xAxis: true,
    grid: true,
    droplines: true,
    fill: true,
    order: true,
    cityLabels: true,
    elev: true,
    countryBars: true
  });
  const [tension, setTension] = useState(0.62);
  const [labelOffset, setLabelOffset] = useState(8);
  const [labelSize, setLabelSize] = useState(15);
  const [dragIdx, setDragIdx] = useState(-1);
  const [overIdx, setOverIdx] = useState(-1);
  const [exporting, setExporting] = useState(false);
  const svgRef = useRef(null);
  const exportCardRef = useRef(null);

  const visibleCities = useMemo(() => cities.filter((city) => city.on), [cities]);

  const stats = useMemo(() => {
    if (!visibleCities.length) {
      return { count: 0, countries: 0, max: 0, min: 0, gain: 0 };
    }

    const elevations = visibleCities.map((city) => city.elev);
    let gain = 0;
    for (let i = 1; i < visibleCities.length; i += 1) {
      gain += Math.max(0, visibleCities[i].elev - visibleCities[i - 1].elev);
    }

    return {
      count: visibleCities.length,
      countries: new Set(visibleCities.map((city) => city.country)).size,
      max: Math.max(...elevations),
      min: Math.min(...elevations),
      gain
    };
  }, [visibleCities]);

  const toggleCity = (id) => {
    setCities((current) => current.map((city) => (city.id === id ? { ...city, on: !city.on } : city)));
  };

  const changeField = (id, field, value) => {
    setCities((current) => current.map((city) => (city.id === id ? { ...city, [field]: value } : city)));
  };

  const deleteCity = (id) => {
    setCities((current) => current.filter((city) => city.id !== id));
  };

  const addCity = () => {
    const nextId = Math.max(0, ...cities.map((city) => city.id)) + 1;
    setCities((current) => current.concat({
      id: nextId,
      name: "새 도시",
      country: "국가 미정",
      elev: 0,
      gps: "0.0000, 0.0000",
      on: true
    }));
  };

  const resetAll = () => {
    if (window.confirm("제공된 표 기준 기본 데이터로 되돌릴까요?")) {
      setCities(INITIAL_CITIES);
    }
  };

  const selectAll = () => setCities((current) => current.map((city) => ({ ...city, on: true })));
  const deselectAll = () => setCities((current) => current.map((city) => ({ ...city, on: false })));

  const applyPreset = (preset) => {
    if (preset === "presentation") {
      setToggles({
        yAxis: true,
        xAxis: true,
        grid: false,
        droplines: false,
        fill: true,
        order: true,
        cityLabels: true,
        elev: true,
        countryBars: true
      });
      setLabelOffset(16);
      setLabelSize(16);
      setTension(0.72);
      return;
    }

    if (preset === "dense") {
      setToggles({
        yAxis: true,
        xAxis: true,
        grid: true,
        droplines: true,
        fill: true,
        order: false,
        cityLabels: true,
        elev: false,
        countryBars: true
      });
      setLabelOffset(2);
      setLabelSize(13);
      setTension(0.58);
      return;
    }

    setToggles({
      yAxis: true,
      xAxis: true,
      grid: true,
      droplines: true,
      fill: true,
      order: true,
      cityLabels: true,
      elev: true,
      countryBars: true
    });
    setLabelOffset(8);
    setLabelSize(15);
    setTension(0.62);
  };

  const downloadPNG = async (scale = 3) => {
    const exportNode = exportCardRef.current;
    if (!exportNode) return;
    if (!window.htmlToImage?.toPng) {
      window.alert("PNG 내보내기 라이브러리를 불러오지 못했습니다.");
      return;
    }

    setExporting(true);
    try {
      if (document.fonts?.ready) {
        await document.fonts.ready;
      }

      const dataUrl = await window.htmlToImage.toPng(exportNode, {
        pixelRatio: scale,
        cacheBust: true,
        backgroundColor: "#f7f3ea",
        skipFonts: false
      });

      const now = new Date();
      const stamp = [
        now.getFullYear(),
        String(now.getMonth() + 1).padStart(2, "0"),
        String(now.getDate()).padStart(2, "0")
      ].join("");
      const time = [
        String(now.getHours()).padStart(2, "0"),
        String(now.getMinutes()).padStart(2, "0")
      ].join("");

      downloadBlob(dataUrl, `남미여행루트_보이는화면_${stamp}_${time}_${scale}x.png`);
    } catch (error) {
      console.error(error);
      window.alert(`PNG 다운로드에 실패했습니다.\n${error.message}`);
    } finally {
      setExporting(false);
    }
  };

  const handleDragStart = (idx, event) => {
    setDragIdx(idx);
    event.dataTransfer.effectAllowed = "move";
  };

  const handleDragOver = (idx, event) => {
    if (event) event.preventDefault();
    setOverIdx(idx);
  };

  const handleDrop = (idx, event) => {
    event.preventDefault();
    if (dragIdx < 0 || dragIdx === idx) {
      setDragIdx(-1);
      setOverIdx(-1);
      return;
    }

    setCities((current) => {
      const next = [...current];
      const [moved] = next.splice(dragIdx, 1);
      next.splice(idx, 0, moved);
      return next;
    });

    setDragIdx(-1);
    setOverIdx(-1);
  };

  return (
    <div className="app-shell">
      <main className="stage">
        <section className="hero">
          <div>
            <div className="eyebrow">South America Route Studio</div>
            <h1>남미 6개국 루트를 표 기준 순서로 다시 정리</h1>
            <p>
              순서는 페루 → 볼리비아 → 칠레 → 아르헨티나 → 우루과이 → 브라질입니다.
              PNG는 SVG만 따로 저장하지 않고, 현재 화면에서 보이는 차트 카드 그대로 캡처합니다.
            </p>
          </div>
          <div className="hero-actions">
            <button type="button" className="primary-btn" onClick={() => downloadPNG(3)} disabled={exporting}>
              {exporting ? "PNG 생성 중..." : "보이는 화면 PNG"}
            </button>
            <button type="button" className="ghost-btn" onClick={() => applyPreset("presentation")}>
              발표형 프리셋
            </button>
          </div>
        </section>

        <section className="summary-grid">
          <article className="summary-card">
            <div className="label">표시 도시</div>
            <div className="value">{stats.count}</div>
            <div className="hint">전체 {cities.length}개 중 현재 차트에 반영되는 도시 수</div>
          </article>
          <article className="summary-card">
            <div className="label">최고 고도</div>
            <div className="value">{stats.max.toLocaleString()}m</div>
            <div className="hint">무지개산과 엘 타티오 같은 고지대가 포함됩니다</div>
          </article>
          <article className="summary-card">
            <div className="label">누적 상승</div>
            <div className="value">{stats.gain.toLocaleString()}m</div>
            <div className="hint">현재 켜진 순서를 기준으로 상승분만 합산합니다</div>
          </article>
          <article className="summary-card">
            <div className="label">국가 묶음</div>
            <div className="value">{stats.countries}</div>
            <div className="hint">혼합 구간은 별도 라벨로 유지합니다</div>
          </article>
        </section>

        <section ref={exportCardRef} className="chart-card export-surface">
          <div className="chart-toolbar">
            <div>
              <h2>고도 프로파일</h2>
              <p>Peru → Bolivia → Chile → Argentina → Uruguay → Brazil 흐름으로 정렬된 여행 루트입니다.</p>
            </div>
            <div className={`status-badge ${exporting ? "is-busy" : ""}`}>
              {exporting ? "화면 그대로 캡처 중" : "모바일 friendly 레이아웃 적용"}
            </div>
          </div>
          <div className="chart-frame">
            <Chart
              cities={cities}
              toggles={toggles}
              tension={tension}
              labelOffset={labelOffset}
              labelSize={labelSize}
              svgRef={svgRef}
            />
          </div>
        </section>
      </main>

      <aside className="panel">
        <section className="panel-card">
          <h3>표현 옵션</h3>
          <p>작은 화면에서도 버튼이 눌리기 쉽도록 토글 간격과 크기를 키웠습니다.</p>
          <div className="toggle-grid">
            {TOGGLE_META.map(([key, label, hint]) => (
              <ToggleChip
                key={key}
                active={toggles[key]}
                label={label}
                hint={hint}
                onClick={() => setToggles((current) => ({ ...current, [key]: !current[key] }))}
              />
            ))}
          </div>
        </section>

        <section className="panel-card">
          <h3>레이아웃 튜닝</h3>
          <p>모바일에서 너무 복잡해지지 않도록 프리셋과 슬라이더를 유지하되 세로 흐름을 단순화했습니다.</p>
          <div className="slider-stack">
            <div className="slider-row">
              <label htmlFor="tension">곡선 강도</label>
              <input id="tension" type="range" min="0" max="1.5" step="0.05" value={tension} onChange={(event) => setTension(Number(event.target.value))} />
              <div className="value">{tension.toFixed(2)}</div>
            </div>
            <div className="slider-row">
              <label htmlFor="offset">라벨 높이</label>
              <input id="offset" type="range" min="-24" max="120" step="1" value={labelOffset} onChange={(event) => setLabelOffset(Number(event.target.value))} />
              <div className="value">{labelOffset}px</div>
            </div>
            <div className="slider-row">
              <label htmlFor="size">라벨 크기</label>
              <input id="size" type="range" min="10" max="24" step="1" value={labelSize} onChange={(event) => setLabelSize(Number(event.target.value))} />
              <div className="value">{labelSize}px</div>
            </div>
          </div>
          <div className="preset-row" style={{ marginTop: 14 }}>
            <button type="button" className="chip-btn" onClick={() => applyPreset("default")}>기본형</button>
            <button type="button" className="chip-btn" onClick={() => applyPreset("presentation")}>발표형</button>
            <button type="button" className="chip-btn" onClick={() => applyPreset("dense")}>촘촘형</button>
          </div>
        </section>

        <section className="panel-card">
          <h3>PNG 내보내기</h3>
          <p>현재 보이는 차트 카드 DOM을 직접 캡처하므로, 그림자와 배경을 포함한 실제 화면 결과를 그대로 저장합니다.</p>
          <div className="export-row">
            <button type="button" className="export-main" onClick={() => downloadPNG(3)} disabled={exporting}>
              {exporting ? "PNG 생성 중..." : "보이는 화면 3x"}
            </button>
            <button type="button" className="scale-btn" onClick={() => downloadPNG(2)} disabled={exporting}>2x</button>
            <button type="button" className="scale-btn" onClick={() => downloadPNG(4)} disabled={exporting}>4x</button>
          </div>
        </section>

        <section className="panel-card">
          <div className="list-head">
            <div>
              <h3>도시 목록</h3>
              <div className="sub">{visibleCities.length}개 도시가 현재 차트에 표시됩니다.</div>
            </div>
            <div className="list-actions">
              <button type="button" className="mini-btn" onClick={selectAll}>전체 선택</button>
              <button type="button" className="mini-btn" onClick={deselectAll}>전체 해제</button>
              <button type="button" className="mini-btn warn" onClick={resetAll}>초기화</button>
            </div>
          </div>
          <div className="city-list">
            {cities.map((city, index) => (
              <CityRow
                key={city.id}
                city={city}
                idx={index}
                isDragging={dragIdx === index}
                isDropTarget={overIdx === index && dragIdx !== index && dragIdx !== -1}
                onToggle={toggleCity}
                onChange={changeField}
                onDelete={deleteCity}
                onDragStart={handleDragStart}
                onDragOver={handleDragOver}
                onDrop={handleDrop}
              />
            ))}
          </div>
          <div className="header-actions" style={{ marginTop: 12 }}>
            <button type="button" className="ghost-btn" onClick={addCity}>+ 도시 추가</button>
          </div>
        </section>

        <section className="panel-card">
          <h3>사용 메모</h3>
          <div className="meta">
            표 기준 순서를 기본값으로 넣었고, GPS는 각 카드 하단에 유지했습니다.
            모바일에서는 차트가 먼저 나오고 편집 패널이 아래로 내려가며,
            PNG는 현재 보이는 카드 모양 그대로 저장되도록 DOM 캡처 방식으로 변경했습니다.
          </div>
        </section>
      </aside>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
