import { useCallback, useEffect, useRef, useState } from "react";
import {
  embeddedSubtitles,
  fetchSubtitles,
  fileUrl,
  listSubtitles,
  openExternal,
  resolveStream,
  savePlayback,
} from "../api";
import {
  DEFAULT_SUB_LANG,
  SUB_FONTS,
  SUB_PLACES,
  type FeedItem,
  type SubStyle,
} from "../types";
import { compactViews, relativeTime, subtitleLabel } from "./format";
import PlayerControls from "./PlayerControls";
import { Badge, BTN, Dialog, Spinner } from "./ui";

interface Props {
  item: FeedItem;
  /** Local file when downloaded; null means resolve and stream it instead. */
  path: string | null;
  onClose: () => void;
  onDelete: () => void;
  onPrev?: () => void;
  onNext?: () => void;
  /** Present only while streaming, so the video can be saved from here. */
  onDownload?: () => void;
  downloading?: boolean;
  /** Max height for streaming, or null to let the player adapt. */
  maxHeight: number | null;
  /** Preferred subtitle language, or "off". */
  subLang: string;
  subStyle: SubStyle;
  onSubStyle: (s: SubStyle) => void;
  /** Leaves the player for this video's channel. */
  onOpenChannel: () => void;
  /** Roll straight on to the next video when this one ends. */
  autoplayNext: boolean;
  /** Persists a subtitle choice made from the transport bar. */
  onSubLang: (l: string) => void;
  /** Position in the current feed, for the "3 of 180" readout. */
  index: number;
  total: number;
  /** False in window fullscreen, where there are no traffic lights to clear. */
  titleBarInset: boolean;
}

/**
 * Ends any Picture-in-Picture session on a <video>.
 *
 * Detaching the element is not enough: WebKit keeps the session (and its
 * audio) running after the element leaves the DOM, so stepping to the next
 * video would leave the previous one playing with no way to stop it.
 */
function releasePiP(v: HTMLVideoElement | null) {
  if (!v) return;
  // Safari's PiP is the non-standard presentation-mode API; the spec one is
  // tried too, since either may be the live implementation.
  const webkit = v as WebkitVideo;
  try {
    if (
      webkit.webkitPresentationMode &&
      webkit.webkitPresentationMode !== "inline"
    ) {
      webkit.webkitSetPresentationMode?.("inline");
    }
  } catch {
    /* not supported here */
  }
  try {
    if (webkit.webkitDisplayingFullscreen) webkit.webkitExitFullscreen?.();
  } catch {
    /* not supported here */
  }
  try {
    if (document.pictureInPictureElement) void document.exitPictureInPicture();
  } catch {
    /* not supported here */
  }
}

/**
 * Releases the element for good, on the way out of the player.
 *
 * Separate from the above because it also leaves fullscreen, and this used to
 * run on every change of video: stepping to the next one dropped you out of
 * fullscreen every time, which is the opposite of watching one after another.
 */
function teardown(v: HTMLVideoElement | null) {
  if (!v) return;
  releasePiP(v);
  try {
    if (document.fullscreenElement) void document.exitFullscreen();
  } catch {
    /* not supported here */
  }
  try {
    v.pause();
    // Dropping the source is what actually frees the decoder and the audio.
    v.removeAttribute("src");
    v.load();
  } catch {
    /* already gone */
  }
}

/** The WebKit-only members we rely on for PiP and native video fullscreen. */
type WebkitVideo = HTMLVideoElement & {
  webkitPresentationMode?: string;
  webkitSetPresentationMode?: (mode: string) => void;
  webkitEnterFullscreen?: () => void;
  webkitExitFullscreen?: () => void;
  webkitDisplayingFullscreen?: boolean;
};

/** Save at most this often while playing; also saved on close. */
const SAVE_EVERY_MS = 5000;
/** Ignore a saved position this close to either end — nothing useful to resume. */
const RESUME_EDGE_S = 5;

/**
 * Two sources, one player element.
 *
 * Downloaded: the local file through Tauri's asset protocol. Every download is
 * H.264/AAC in MP4 precisely so WKWebView can decode it natively.
 *
 * Not downloaded: YouTube's HLS master playlist, resolved by yt-dlp. Its
 * variants are H.264 + AAC up to 1080p, which AVFoundation streams natively —
 * so watching still happens here rather than in a browser. The iframe embed
 * cannot be used: it rejects a `tauri://` origin with "Error 153".
 */
/** Web addresses in a YouTube description, which are plain text as it stores them. */
const URL_IN_TEXT = /(https?:\/\/[^\s<>"']+)/g;

/**
 * A description with its addresses made clickable.
 *
 * They open in the real browser: a YouTube link is the one thing in here this
 * app has no way to show, and the rest belong to whoever wrote them.
 */
function Linked({ text }: { text: string }) {
  return (
    <>
      {text.split(URL_IN_TEXT).map((part, i) =>
        // Not URL_IN_TEXT.test: a global regex carries lastIndex between calls,
        // so every other address would come out as plain text.
        part.startsWith("http") ? (
          // Trailing punctuation is sentence, not address.
          <button
            key={i}
            onClick={() => openExternal(part.replace(/[.,;:!?)\]]+$/, ""))}
            title={part}
            className="cursor-pointer break-all text-left text-sky-600 underline
                       underline-offset-2 hover:text-sky-500 dark:text-sky-400"
          >
            {part}
          </button>
        ) : (
          <span key={i}>{part}</span>
        ),
      )}
    </>
  );
}

/**
 * Rewrites every cue's settings to one placement.
 *
 * WebVTT carries position on the cue itself, so this is the only way to move
 * subtitles: no CSS property places them. Existing settings are dropped rather
 * than merged — YouTube's own are exactly what needs overriding.
 */
function placeCues(vtt: string, line: number | null): string {
  return vtt.replace(
    /^(\s*[\d:.]+\s+-->\s+[\d:.]+)(.*)$/gm,
    (_m, times: string) => (line == null ? times : `${times} line:${line}%`),
  );
}

export default function Player({
  item,
  path,
  onClose,
  onDelete,
  onPrev,
  onNext,
  onDownload,
  downloading,
  maxHeight,
  subLang,
  onSubLang,
  subStyle,
  onSubStyle,
  onOpenChannel,
  autoplayNext,
  index,
  total,
  titleBarInset,
}: Props) {
  const streaming = path === null;
  const [src, setSrc] = useState<string | null>(path ? fileUrl(path) : null);
  const [error, setError] = useState<string | null>(null);
  const [buffering, setBuffering] = useState(true);
  // The height actually being decoded. With an adaptive stream this changes as
  // the player switches rendition, so it is read from the element rather than
  // assumed from the setting.
  const [height, setHeight] = useState(0);
  /**
   * Subtitle tracks as (language, URL), from whichever source has them:
   * WebVTT files yt-dlp saved beside a download, or a fetch.
   *
   * A stream has no other source — YouTube's HLS manifest carries a dozen
   * audio renditions and no subtitles at all — and a download saved before
   * subtitles were switched on has nothing beside it either.
   *
   * Fetching does not depend on the Settings preference. That preference says
   * which language is switched on by itself; it must not decide whether
   * subtitles exist to be chosen at all, or "None" would quietly empty the
   * player's subtitle menu as well.
   *
   * Fetched cues become blob URLs, which share the document's origin — a
   * file:// or 127.0.0.1 track would not.
   */
  const [rawTracks, setRawTracks] = useState<Array<[string, string]>>([]);
  const [tracks, setTracks] = useState<Array<[string, string]>>([]);
  const [fetchingSubs, setFetchingSubs] = useState(false);
  const [showDescription, setShowDescription] = useState(false);
  // The language to fetch, which is NOT the preference: turning subtitles on
  // from the player's menu moves the preference from "off" to that language,
  // and refetching then would tear down the tracks — and the stream with them —
  // the instant one is chosen.
  const wantLang = subLang === "off" ? DEFAULT_SUB_LANG : subLang;

  useEffect(() => {
    let cancelled = false;
    setRawTracks([]);
    setFetchingSubs(false);

    const load = async () => {
      if (path) {
        // Muxed into the download, which is where they belong — but read out
        // rather than left to the element's in-band rendering, which the media
        // pipeline draws wherever the container's text box points and which no
        // styling can reach.
        const inside = await embeddedSubtitles(item.id).catch(() => []);
        if (cancelled) return;
        if (inside.length > 0) {
          setRawTracks(inside);
          return;
        }
        // Downloads from before subtitles were embedded kept them beside the
        // file instead.
        const local = await listSubtitles(item.id).catch(() => []);
        if (cancelled) return;
        if (local.length > 0) {
          setRawTracks(local);
          return;
        }
      }
      if (cancelled) return;
      setFetchingSubs(true);
      try {
        const list = await fetchSubtitles(item.id, wantLang);
        if (cancelled) return;
        setRawTracks(list);
      } catch {
        /* a video with no captions in this language is an ordinary outcome */
      } finally {
        if (!cancelled) setFetchingSubs(false);
      }
    };
    void load();

    return () => {
      cancelled = true;
    };
  }, [item.id, path, wantLang]);

  // Placement is a WebVTT cue setting, not something CSS can reach, so it is
  // written into the cues themselves. Re-cut whenever the choice changes.
  useEffect(() => {
    const line =
      SUB_PLACES.find((p) => p.value === subStyle.place)?.line ?? null;
    const urls: string[] = [];
    setTracks(
      rawTracks.map(([lang, text]) => {
        const url = URL.createObjectURL(
          new Blob([placeCues(text, line)], { type: "text/vtt" }),
        );
        urls.push(url);
        return [lang, url] as [string, string];
      }),
    );
    return () => {
      for (const u of urls) URL.revokeObjectURL(u);
    };
  }, [rawTracks, subStyle.place]);

  // What a remount used to clear. Kept in one place so a new video starts as
  // clean as it would have, without throwing the stage away to get there.
  useEffect(() => {
    setError(null);
    setBuffering(true);
    setShowDescription(false);
  }, [item.id]);

  // Fullscreen belongs to the stage, which now outlives the video. Whether it
  // is on decides whether anything to click is shown at all.
  const [fullscreen, setFullscreen] = useState(false);
  useEffect(() => {
    const sync = () => setFullscreen(!!document.fullscreenElement);
    sync();
    document.addEventListener("fullscreenchange", sync);
    return () => document.removeEventListener("fullscreenchange", sync);
  }, []);

  // Controls and edge arrows fade away while you are just watching.
  const [chromeVisible, setChromeVisible] = useState(true);
  const hideTimer = useRef<number | undefined>(undefined);
  const videoRef = useRef<HTMLVideoElement>(null);
  // Quality is the short side, so a portrait Short reads 1080p, not 1920p.
  const measure = useCallback(() => {
    const v = videoRef.current;
    if (!v?.videoHeight) return;
    setHeight(Math.min(v.videoWidth, v.videoHeight));
  }, []);
  const stageRef = useRef<HTMLDivElement>(null);
  const lastSave = useRef(0);

  useEffect(() => {
    setHeight(0);
    if (path) {
      setSrc(fileUrl(path));
      return;
    }
    let cancelled = false;
    setError(null);
    // Quiet the outgoing video, but leave it in place: clearing the source
    // would unmount the element mid-fullscreen.
    videoRef.current?.pause();
    setBuffering(true);
    resolveStream(item.id, maxHeight)
      .then((u) => !cancelled && setSrc(u))
      .catch((e) => !cancelled && setError(String(e)));
    return () => {
      cancelled = true;
    };
  }, [item.id, path, maxHeight]);

  const persist = useCallback(() => {
    const v = videoRef.current;
    if (!v || !Number.isFinite(v.duration) || v.duration <= 0) return;
    savePlayback(item.id, v.currentTime, v.duration).catch(() => {
      /* a lost position is not worth interrupting playback for */
    });
  }, [item.id]);

  // Save on the way out, including when switching to another video. Declared
  // before the teardown effect so React runs this cleanup first — the position
  // has to be read off the element before its source is dropped.
  useEffect(() => () => persist(), [persist]);

  useEffect(() => {
    const v = videoRef.current;
    return () => releasePiP(v);
  }, [src]);

  // Leaving the player is the only place the element is released outright.
  useEffect(() => {
    const v = videoRef.current;
    return () => teardown(v);
  }, []);

  const onTimeUpdate = () => {
    // Frames are flowing, so whatever the media events claimed, we are not
    // buffering. A resume-seek can fire `waiting` after `playing` and leave the
    // spinner stuck otherwise.
    const v = videoRef.current;
    if (v && v.readyState >= 3 && !v.paused) setBuffering(false);

    const now = Date.now();
    if (now - lastSave.current < SAVE_EVERY_MS) return;
    lastSave.current = now;
    persist();
  };

  // Pick up where this video was left off, unless that was right at either end.
  const onLoadedMetadata = () => {
    const v = videoRef.current;
    const at = item.position ?? 0;
    if (!v || !Number.isFinite(v.duration)) return;
    if (at > RESUME_EDGE_S && at < v.duration - RESUME_EDGE_S)
      v.currentTime = at;
  };

  const leave = useCallback(() => onClose(), [onClose]);

  // Escape backs out, as it does everywhere else in the app.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      // In fullscreen, Escape is the transport bar's to handle: it leaves
      // fullscreen. Closing the player as well would drop you all the way back
      // to the feed in one keypress.
      if (e.key === "Escape" && !document.fullscreenElement) leave();
      // Moving between videos. Shift with the arrows because the bare ones
      // seek, and shift with N and P because that is what YouTube uses.
      if (
        e.shiftKey &&
        (e.key === "ArrowLeft" || e.key === "P" || e.key === "p")
      )
        onPrev?.();
      if (
        e.shiftKey &&
        (e.key === "ArrowRight" || e.key === "N" || e.key === "n")
      )
        onNext?.();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [leave, onPrev, onNext]);

  const edgeBtn =
    "absolute top-1/2 z-10 -translate-y-1/2 grid size-11 place-items-center rounded-full " +
    "bg-slate-950/55 text-2xl leading-none text-white backdrop-blur cursor-pointer " +
    "transition-opacity duration-200 hover:bg-slate-950/80 disabled:hidden " +
    (chromeVisible ? "opacity-100" : "pointer-events-none opacity-0");

  const showChrome = useCallback(() => {
    setChromeVisible(true);
    window.clearTimeout(hideTimer.current);
    hideTimer.current = window.setTimeout(() => {
      // Never hide while paused — there would be no way back to play.
      if (!videoRef.current?.paused) setChromeVisible(false);
    }, 2600);
  }, []);

  useEffect(() => {
    showChrome();
    return () => window.clearTimeout(hideTimer.current);
  }, [showChrome, src]);

  const navIcon =
    "grid h-[30px] w-[30px] shrink-0 place-items-center rounded-lg border border-slate-300 " +
    "cursor-pointer hover:border-sky-500 hover:text-sky-600 disabled:opacity-30 " +
    "disabled:cursor-not-allowed dark:border-slate-700 dark:hover:border-sky-500 " +
    "dark:hover:text-sky-400";

  const navBtn =
    "inline-flex h-[30px] items-center rounded-lg border border-slate-300 px-2.5 text-[12px] font-medium cursor-pointer " +
    "hover:border-sky-500 hover:text-sky-600 disabled:opacity-30 disabled:cursor-not-allowed " +
    "disabled:hover:border-slate-300 disabled:hover:text-inherit " +
    "dark:border-slate-700 dark:hover:border-sky-500 dark:hover:text-sky-400";

  return (
    <div className="fixed inset-0 z-50 flex flex-col bg-slate-100 dark:bg-slate-950">
      {titleBarInset && <div data-tauri-drag-region className="h-9 shrink-0" />}
      <header
        className="flex items-center gap-2 border-b border-slate-200 bg-white px-4 py-3
                   dark:border-slate-800 dark:bg-slate-900"
      >
        <button
          onClick={leave}
          title="Back to the feed (Esc)"
          aria-label="Back"
          className={navIcon}
        >
          <svg
            viewBox="0 0 24 24"
            className="size-4"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M15 6l-6 6 6 6"
            />
          </svg>
        </button>

        <button
          onClick={onPrev}
          disabled={!onPrev}
          title="Previous video"
          className={navBtn}
        >
          ‹ Prev
        </button>
        <button
          onClick={onNext}
          disabled={!onNext}
          title="Next video"
          className={navBtn}
        >
          Next ›
        </button>
        <span className="font-mono text-[11px] tabular-nums text-slate-400 dark:text-slate-500">
          {index + 1}/{total}
        </span>

        {/* A tag rather than a caption: it goes somewhere. */}
        <div className="flex min-w-0 flex-1 items-center">
          <button
            onClick={onOpenChannel}
            title={`Show everything from ${item.channel_title}`}
            className="max-w-full cursor-pointer truncate rounded-full border border-slate-300
                       px-2.5 py-0.5 text-[12px] text-slate-500 transition-colors
                       hover:border-sky-500 hover:text-sky-600 dark:border-slate-700
                       dark:text-slate-400 dark:hover:border-sky-500 dark:hover:text-sky-400"
          >
            {item.channel_title}
          </button>
        </div>

        {streaming ? (
          error ? (
            <Badge tone="danger">Unavailable</Badge>
          ) : src && !buffering ? (
            <Badge
              tone="accent"
              title={`Streaming at ${height || "an unknown"}p`}
            >
              Streaming{height ? ` · ${height}p` : ""}
            </Badge>
          ) : (
            <Badge>Loading…</Badge>
          )
        ) : (
          height > 0 && (
            <Badge title={`This copy on your Mac is ${height}p`}>
              Downloaded · {height}p
            </Badge>
          )
        )}
      </header>

      <style>{`video::cue {
        font-size: ${subStyle.size}%;
        font-family: ${SUB_FONTS.find((f) => f.value === subStyle.font)?.stack ?? "sans-serif"};
      }`}</style>

      {/* Absolute fill + object-contain, so portrait Shorts and landscape
          videos are both letterboxed to the pane instead of overflowing it. */}
      <div
        ref={stageRef}
        onContextMenu={(e) => e.preventDefault()}
        onMouseMove={showChrome}
        onMouseLeave={() =>
          !videoRef.current?.paused && setChromeVisible(false)
        }
        className={`relative min-h-0 flex-1 bg-slate-950 ${chromeVisible ? "" : "cursor-none"}`}
      >
        {/* Edge arrows, the way a player wants them: big targets on the left and
            right of the picture. They fade in on hover so they never sit on top
            of the video while you are watching it.

            Gone entirely in fullscreen. Nothing to navigate with there: it
            plays one video after another and shows only the picture. */}
        {!fullscreen && (
          <>
            <button
              onClick={onPrev}
              disabled={!onPrev}
              title="Previous video"
              aria-label="Previous video"
              className={`${edgeBtn} left-3`}
            >
              ‹
            </button>
            <button
              onClick={onNext}
              disabled={!onNext}
              title="Next video"
              aria-label="Next video"
              className={`${edgeBtn} right-3`}
            >
              ›
            </button>
          </>
        )}

        {/* Resolving the stream and buffering it are the same wait as far as
            you are concerned, so they get the same spinner in the same place. */}
        {(!src || buffering) && !error && (
          <div className="pointer-events-none absolute inset-0 z-30 grid place-items-center">
            <Spinner className="size-8 text-white/80" />
          </div>
        )}

        {src ? (
          <video
            ref={videoRef}
            src={src}
            autoPlay
            onContextMenu={(e) => e.preventDefault()}
            onClick={() => {
              const v = videoRef.current;
              if (!v) return;
              if (v.paused) void v.play().catch(() => {});
              else v.pause();
            }}
            onTimeUpdate={onTimeUpdate}
            onLoadedMetadata={onLoadedMetadata}
            onPause={persist}
            onEnded={() => {
              persist();
              // Only where there is somewhere to go: the last video in the
              // list simply stops.
              if (autoplayNext) onNext?.();
            }}
            onLoadStart={() => setBuffering(true)}
            onWaiting={() => setBuffering(true)}
            onStalled={() => setBuffering(true)}
            onCanPlay={() => setBuffering(false)}
            onPlaying={() => setBuffering(false)}
            onSeeked={() => setBuffering(false)}
            onResize={measure}
            onLoadedData={measure}
            className="absolute inset-0 size-full object-contain"
          >
            {tracks.map(([lang, url]) => (
              <track
                key={url}
                kind="subtitles"
                srcLang={lang}
                label={subtitleLabel(lang)}
                src={url}
              />
            ))}
          </video>
        ) : (
          error && (
            <div className="absolute inset-0 grid place-items-center px-6 text-center">
              <div className="max-w-sm">
                <p className="text-[13px] text-red-400">{error}</p>
                <button
                  onClick={() =>
                    openExternal(`https://www.youtube.com/watch?v=${item.id}`)
                  }
                  className={`${BTN} mt-3 cursor-pointer py-1.5`}
                >
                  Open on YouTube instead
                </button>
              </div>
            </div>
          )
        )}
        {src && !error && (
          <div
            className={`transition-opacity duration-200 ${
              chromeVisible ? "opacity-100" : "pointer-events-none opacity-0"
            }`}
          >
            <PlayerControls
              videoRef={videoRef}
              stageRef={stageRef}
              onActivity={showChrome}
              subLang={subLang}
              onSubLang={onSubLang}
              subStyle={subStyle}
              onSubStyle={onSubStyle}
              subsLoading={fetchingSubs}
            />
          </div>
        )}
      </div>

      <footer
        className="max-h-52 shrink-0 overflow-y-auto border-t border-slate-200 bg-white px-4 py-3
                   dark:border-slate-800 dark:bg-slate-900"
      >
        <div className="flex items-start justify-between gap-3">
          {/* Title and figures on one line. The title opens the description
              rather than announcing itself as a link: it keeps its colour and
              stays unadorned, and the pointer says the rest. */}
          <div className="flex min-w-0 items-baseline gap-2">
            {item.description ? (
              <button
                onClick={() => setShowDescription(true)}
                title="Show the description"
                className="min-w-0 cursor-pointer truncate text-left text-[15px] font-semibold
                           tracking-tight"
              >
                {item.title}
              </button>
            ) : (
              <h2 className="min-w-0 truncate text-[15px] font-semibold tracking-tight">
                {item.title}
              </h2>
            )}
            <span className="shrink-0 text-[11px] text-slate-400 dark:text-slate-500">
              {[compactViews(item.views), relativeTime(item.published)]
                .filter(Boolean)
                .join(" · ")}
            </span>
          </div>
          <div className="flex shrink-0 items-center gap-2">
            {onDownload && (
              <button
                onClick={onDownload}
                disabled={downloading}
                title={downloading ? "Downloading…" : "Download for offline"}
                aria-label="Download"
                className={navIcon}
              >
                {downloading ? (
                  <Spinner className="size-4" />
                ) : (
                  <svg
                    viewBox="0 0 24 24"
                    className="size-4"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                  >
                    <path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      d="M12 4v12m0 0l-4-4m4 4l4-4M4 20h16"
                    />
                  </svg>
                )}
              </button>
            )}
            {!streaming && (
              <button
                onClick={onDelete}
                title="Delete this download"
                aria-label="Delete download"
                className={`${navIcon} hover:border-red-500! hover:text-red-600!
                            dark:hover:border-red-500! dark:hover:text-red-400!`}
              >
                <svg
                  viewBox="0 0 24 24"
                  className="size-4"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13M10 11v6M14 11v6"
                  />
                </svg>
              </button>
            )}
            <button
              onClick={() =>
                openExternal(`https://www.youtube.com/watch?v=${item.id}`)
              }
              title="Open on YouTube"
              aria-label="Open on YouTube"
              className={navIcon}
            >
              <svg
                viewBox="0 0 24 24"
                className="size-4"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M14 4h6v6M20 4l-9 9M18 14v5a1 1 0 01-1 1H5a1 1 0 01-1-1V7a1 1 0 011-1h5"
                />
              </svg>
            </button>
          </div>
        </div>
      </footer>

      {showDescription && (
        <Dialog
          title={item.title}
          onCancel={() => setShowDescription(false)}
          wide
        >
          <p className="whitespace-pre-wrap text-[12.5px] leading-relaxed">
            <Linked text={item.description} />
          </p>
        </Dialog>
      )}
    </div>
  );
}
