<?php
/**
 * post.php — Browser admin UI for updates.json with media upload support
 * ─────────────────────────────────────────────────────────────────────────────
 * Requires: api.php · media_upload.php · index_config/api_keys.txt
 * Media saved to: /media/  (configured in media_upload.php)
 */

// ══════════════════════════════════════════════════════════════════════════════
// CONFIG
// ══════════════════════════════════════════════════════════════════════════════
define('POST_API_URL',    './api.php');
define('POST_UPLOAD_URL', './media_upload.php');
define('POST_PAGE_TITLE', 'Updates — Post Manager');
// ══════════════════════════════════════════════════════════════════════════════
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><?= htmlspecialchars(POST_PAGE_TITLE) ?></title>
  <meta name="robots" content="noindex, nofollow">

  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Share+Tech+Mono&display=swap" rel="stylesheet">

  <style>
    /* ── Design tokens — exact gate.php match ───────────────────────────────── */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --bg:         #09090f;
      --surface:    #0f0f1a;
      --surface-2:  #141425;
      --border:     #1e1e38;
      --border-2:   #2a2a48;
      --text:       #e8e8f8;
      --text-2:     #9898c0;
      --text-3:     #58587a;
      --green:      #00e87a;
      --blue:       #00c8ff;
      --purple:     #b060ff;
      --red:        #ff3355;
      --orange:     #ff8800;
      --pink:       #ff50a0;
      --pat:        #ff424d;
      --pat-glow:   rgba(255,66,77,.18);
      --r:          6px;
    }

    html { font-size: 16px; -webkit-text-size-adjust: 100%; }

    body {
      min-height: 100dvh;
      background: var(--bg);
      background-image:
        radial-gradient(ellipse 70% 50% at 20% 10%, rgba(176,96,255,.05) 0%, transparent 60%),
        radial-gradient(ellipse 60% 40% at 80% 80%, rgba(0,200,255,.04)  0%, transparent 60%),
        radial-gradient(ellipse 50% 60% at 50% 50%, rgba(255,66,77,.03)  0%, transparent 70%);
      font-family: 'Inter', system-ui, sans-serif;
      color: var(--text);
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 1.5rem 1rem 3rem;
    }

    body::after {
      content: '';
      position: fixed; inset: 0; pointer-events: none; z-index: 999;
      background: repeating-linear-gradient(
        0deg, transparent 0px, transparent 3px,
        rgba(0,0,0,.07) 3px, rgba(0,0,0,.07) 4px
      );
    }

    @keyframes rise {
      from { opacity: 0; transform: translateY(14px) scale(.98); }
      to   { opacity: 1; transform: translateY(0) scale(1); }
    }
    @keyframes shimmer {
      from { background-position:   0% 50%; }
      to   { background-position: 200% 50%; }
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    @keyframes shake {
      0%,100% { transform: translateX(0); }
      25% { transform: translateX(-5px); }
      75% { transform: translateX(5px); }
    }
    @keyframes fade-in {
      from { opacity: 0; transform: translateY(6px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    /* ── Page wrapper ───────────────────────────────────────────────────────── */
    .page {
      width: 100%; max-width: 760px;
      display: flex; flex-direction: column; gap: 1.25rem;
      animation: rise .4s cubic-bezier(.22,.68,0,1.2) both;
      position: relative; z-index: 1;
    }

    /* ── Card ───────────────────────────────────────────────────────────────── */
    .card {
      background: rgba(15,15,26,.88);
      border: 1px solid var(--border);
      border-radius: calc(var(--r) + 4px);
      overflow: hidden; position: relative;
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      box-shadow: 0 0 0 1px rgba(255,255,255,.03), 0 6px 40px rgba(0,0,0,.6);
    }
    .card::before {
      content: '';
      position: absolute; top: 0; left: 0; right: 0; height: 2px; z-index: 2;
      background: linear-gradient(90deg,
        var(--pat), var(--purple), var(--blue), var(--green),
        var(--blue), var(--purple), var(--pat));
      background-size: 300% 100%;
      animation: shimmer 5s linear infinite;
    }
    .card::after {
      content: '';
      position: absolute; bottom: -1px; right: -1px;
      width: 14px; height: 14px;
      border-bottom: 2px solid var(--purple);
      border-right: 2px solid var(--purple);
      border-radius: 0 0 calc(var(--r) + 4px) 0;
      opacity: .5;
    }

    /* ── Top bar ────────────────────────────────────────────────────────────── */
    .topbar {
      display: flex; align-items: center; justify-content: space-between;
      flex-wrap: wrap; gap: .75rem; padding: 1rem 1.25rem;
    }
    .topbar-brand { display: flex; flex-direction: column; gap: .15rem; }
    .topbar-eyebrow {
      font-family: 'Share Tech Mono', monospace;
      font-size: .6rem; letter-spacing: .2em; text-transform: uppercase;
      color: var(--green); text-shadow: 0 0 10px rgba(0,232,122,.5);
    }
    .topbar-eyebrow::before { content: '> '; opacity: .5; }
    .topbar-title { font-size: 1.05rem; font-weight: 700; letter-spacing: -.01em; }
    .topbar-actions { display: flex; align-items: center; gap: .5rem; }

    /* ── Auth ───────────────────────────────────────────────────────────────── */
    #auth-screen {
      padding: 2rem 1.75rem 2.25rem;
      display: flex; flex-direction: column; gap: 1.25rem;
    }
    .auth-heading {
      font-size: 1.25rem; font-weight: 700; letter-spacing: -.01em;
      background: linear-gradient(135deg, var(--blue), var(--purple));
      -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
    }
    .auth-sub { font-family: 'Share Tech Mono', monospace; font-size: .72rem; color: var(--text-3); }

    /* ── Form elements ──────────────────────────────────────────────────────── */
    .field { display: flex; flex-direction: column; gap: .4rem; }
    .field label {
      font-family: 'Share Tech Mono', monospace;
      font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
      color: var(--blue); text-shadow: 0 0 8px rgba(0,200,255,.35);
    }
    .field input[type="text"],
    .field input[type="password"],
    .field input[type="url"],
    .field textarea {
      width: 100%; padding: .72rem .95rem;
      font-family: 'Share Tech Mono', monospace; font-size: .88rem;
      color: var(--text); background: rgba(255,255,255,.03);
      border: 1px solid var(--border-2); border-radius: var(--r); outline: none;
      transition: border-color .2s, box-shadow .2s, background .2s;
      resize: vertical;
    }
    .field input:focus, .field textarea:focus {
      border-color: var(--blue); background: rgba(0,200,255,.04);
      box-shadow: 0 0 0 3px rgba(0,200,255,.1);
    }
    .field input::placeholder, .field textarea::placeholder { color: var(--text-3); }
    .field textarea { min-height: 90px; line-height: 1.6; }
    .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
    @media (max-width: 520px) { .field-row { grid-template-columns: 1fr; } }
    .field-hint { font-family: 'Share Tech Mono', monospace; font-size: .6rem; color: var(--text-3); }

    /* ── File drop zone ─────────────────────────────────────────────────────── */
    .drop-zone {
      position: relative;
      border: 2px dashed var(--border-2);
      border-radius: var(--r);
      padding: 1.25rem 1rem;
      text-align: center;
      cursor: pointer;
      transition: border-color .2s, background .2s;
      background: rgba(255,255,255,.02);
    }
    .drop-zone.drag-over {
      border-color: var(--green);
      background: rgba(0,232,122,.05);
    }
    .drop-zone input[type="file"] {
      position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
    }
    .drop-zone-label {
      pointer-events: none;
      display: flex; flex-direction: column; align-items: center; gap: .5rem;
    }
    .drop-zone-icon { color: var(--text-3); }
    .drop-zone-text {
      font-family: 'Share Tech Mono', monospace;
      font-size: .68rem; letter-spacing: .08em; color: var(--text-3);
    }
    .drop-zone-hint { font-size: .62rem; color: var(--text-3); opacity: .6; }

    /* ── Upload progress bar ────────────────────────────────────────────────── */
    .upload-progress {
      display: none;
      height: 3px;
      background: var(--border-2);
      border-radius: 99px;
      overflow: hidden;
      margin-top: .4rem;
    }
    .upload-progress.visible { display: block; }
    .upload-progress-bar {
      height: 100%;
      background: linear-gradient(90deg, var(--green), var(--blue));
      border-radius: 99px;
      transition: width .2s ease;
      width: 0%;
    }

    /* ── Media preview grid (in compose / edit forms) ───────────────────────── */
    .media-previews {
      display: flex;
      flex-wrap: wrap;
      gap: .5rem;
      margin-top: .25rem;
    }
    .media-preview-item {
      position: relative;
      border: 1px solid var(--border-2);
      border-radius: var(--r);
      overflow: hidden;
      width: 90px; height: 70px;
      background: var(--surface-2);
      flex-shrink: 0;
    }
    .media-preview-item img,
    .media-preview-item video {
      width: 100%; height: 100%;
      object-fit: cover;
      display: block;
    }
    .media-preview-item .media-remove {
      position: absolute; top: 3px; right: 3px;
      width: 18px; height: 18px;
      background: rgba(255,51,85,.85);
      border: none; border-radius: 50%;
      color: #fff; cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-size: 10px; font-weight: 700;
      line-height: 1;
      transition: background .15s;
    }
    .media-preview-item .media-remove:hover { background: var(--red); }
    .media-preview-type {
      position: absolute; bottom: 3px; left: 4px;
      font-family: 'Share Tech Mono', monospace;
      font-size: .48rem; letter-spacing: .06em; text-transform: uppercase;
      color: rgba(255,255,255,.8);
      background: rgba(0,0,0,.55);
      padding: .05em .3em; border-radius: 2px;
    }

    /* ── Buttons ────────────────────────────────────────────────────────────── */
    .btn {
      display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
      padding: .6rem 1.1rem; border-radius: var(--r);
      font-family: 'Inter', sans-serif; font-size: .84rem; font-weight: 600;
      border: none; cursor: pointer; text-decoration: none; white-space: nowrap;
      transition: transform .15s, filter .15s, box-shadow .2s;
      min-height: 38px; position: relative; overflow: hidden;
    }
    .btn::before {
      content: ''; position: absolute; inset: 0;
      background: linear-gradient(180deg, rgba(255,255,255,.09) 0%, transparent 60%);
      pointer-events: none;
    }
    .btn:hover  { transform: translateY(-1px); filter: brightness(1.08); }
    .btn:active { transform: scale(.97); filter: brightness(.95); }
    .btn:disabled { opacity: .45; cursor: not-allowed; transform: none; filter: none; }
    .btn-green  { background: var(--green);  color: #050510; box-shadow: 0 4px 16px rgba(0,232,122,.25); }
    .btn-blue   { background: var(--blue);   color: #050510; box-shadow: 0 4px 14px rgba(0,200,255,.2); }
    .btn-red    { background: var(--red);    color: #fff;    box-shadow: 0 4px 14px rgba(255,51,85,.25); }
    .btn-ghost  {
      background: rgba(255,255,255,.04); color: var(--text-2);
      border: 1px solid var(--border-2);
    }
    .btn-ghost:hover { border-color: var(--green); color: var(--text); }
    .btn-sm { padding: .35rem .75rem; font-size: .76rem; min-height: 30px; }
    .btn .spinner {
      width: 13px; height: 13px; border-radius: 50%;
      border: 2px solid rgba(0,0,0,.2); border-top-color: currentColor;
      animation: spin .6s linear infinite;
    }

    /* ── Alerts ─────────────────────────────────────────────────────────────── */
    .alert {
      display: flex; align-items: flex-start; gap: .6rem;
      border-radius: var(--r); padding: .7rem 1rem;
      font-family: 'Share Tech Mono', monospace; font-size: .78rem; line-height: 1.55;
    }
    .alert-error  { background: rgba(255,51,85,.08); border: 1px solid rgba(255,51,85,.3); color: var(--red); animation: shake .35s ease; }
    .alert-success{ background: rgba(0,232,122,.07); border: 1px solid rgba(0,232,122,.25); color: var(--green); }

    /* ── Section head ───────────────────────────────────────────────────────── */
    .section-head {
      display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .5rem;
      padding: .85rem 1.25rem .7rem; border-bottom: 1px solid var(--border);
    }
    .section-title {
      font-family: 'Share Tech Mono', monospace;
      font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
      color: var(--green); text-shadow: 0 0 10px rgba(0,232,122,.5);
    }
    .section-title::before { content: '> '; opacity: .5; }
    .section-badge { font-family: 'Share Tech Mono', monospace; font-size: .58rem; color: var(--text-3); }

    /* ── Compose ────────────────────────────────────────────────────────────── */
    #compose-form {
      padding: 1rem 1.25rem 1.25rem;
      display: flex; flex-direction: column; gap: .85rem;
    }
    .compose-actions {
      display: flex; align-items: center; justify-content: space-between; gap: .5rem; flex-wrap: wrap;
    }

    /* ── Post list ──────────────────────────────────────────────────────────── */
    #post-list { list-style: none; }
    .post-item {
      border-bottom: 1px solid var(--border);
      padding: 1rem 1.25rem;
      display: flex; flex-direction: column; gap: .6rem;
      animation: fade-in .25s ease both;
    }
    .post-item:last-child { border-bottom: none; }
    .post-meta { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
    .post-id {
      font-family: 'Share Tech Mono', monospace; font-size: .6rem; color: var(--text-3);
      background: rgba(255,255,255,.04); border: 1px solid var(--border-2);
      border-radius: 3px; padding: .05em .4em;
    }
    .post-date { font-family: 'Share Tech Mono', monospace; font-size: .6rem; color: var(--text-3); }
    .post-tag {
      font-family: 'Share Tech Mono', monospace; font-size: .58rem; letter-spacing: .07em;
      text-transform: uppercase; padding: .05em .4em;
      border: 1px solid var(--border-2); border-radius: 3px; color: var(--blue);
    }
    .post-text { font-size: .9rem; color: var(--text); line-height: 1.65; }
    .post-link {
      font-family: 'Share Tech Mono', monospace; font-size: .65rem;
      color: var(--blue); text-decoration: none; opacity: .8;
    }
    .post-link:hover { opacity: 1; }

    /* ── Media strip in feed (admin view — compact) ─────────────────────────── */
    .post-media-strip {
      display: flex; flex-wrap: wrap; gap: .4rem;
    }
    .post-media-thumb {
      width: 72px; height: 56px;
      border: 1px solid var(--border-2); border-radius: 4px;
      overflow: hidden; background: var(--surface-2);
      position: relative; flex-shrink: 0;
    }
    .post-media-thumb img {
      width: 100%; height: 100%; object-fit: cover; display: block;
    }
    .post-media-thumb .vid-icon {
      position: absolute; inset: 0;
      display: flex; align-items: center; justify-content: center;
      background: rgba(0,0,0,.55);
      color: var(--green);
    }
    .post-media-thumb .media-thumb-label {
      position: absolute; bottom: 2px; left: 3px;
      font-family: 'Share Tech Mono', monospace; font-size: .45rem;
      color: rgba(255,255,255,.7); background: rgba(0,0,0,.5);
      padding: .05em .25em; border-radius: 2px; text-transform: uppercase;
    }

    .post-actions { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

    /* ── Inline edit form ───────────────────────────────────────────────────── */
    .edit-form {
      display: none; flex-direction: column; gap: .75rem;
      padding-top: .75rem; border-top: 1px solid var(--border); margin-top: .25rem;
    }
    .edit-form.open { display: flex; animation: fade-in .2s ease; }

    /* ── Misc ───────────────────────────────────────────────────────────────── */
    /* ── Search bar ─────────────────────────────────────────────────────────── */
    .search-wrap {
      padding: .65rem 1.25rem;
      border-bottom: 1px solid var(--border);
      position: relative;
    }
    .search-input-wrap {
      position: relative;
      display: flex;
      align-items: center;
    }
    .search-icon {
      position: absolute;
      left: .75rem;
      color: var(--text-3);
      pointer-events: none;
      flex-shrink: 0;
    }
    .search-input {
      width: 100%;
      padding: .55rem .9rem .55rem 2.2rem;
      font-family: 'Share Tech Mono', monospace;
      font-size: .82rem;
      color: var(--text);
      background: rgba(255,255,255,.03);
      border: 1px solid var(--border-2);
      border-radius: var(--r);
      outline: none;
      transition: border-color .2s, box-shadow .2s, background .2s;
    }
    .search-input:focus {
      border-color: var(--blue);
      background: rgba(0,200,255,.04);
      box-shadow: 0 0 0 3px rgba(0,200,255,.1);
    }
    .search-input::placeholder { color: var(--text-3); }
    .search-clear {
      position: absolute;
      right: .6rem;
      background: none;
      border: none;
      cursor: pointer;
      color: var(--text-3);
      padding: .2rem;
      display: none;
      align-items: center;
      transition: color .15s;
    }
    .search-clear:hover { color: var(--red); }
    .search-clear.visible { display: flex; }
    /* highlight matched text */
    mark {
      background: rgba(0,200,255,.2);
      color: var(--blue);
      border-radius: 2px;
      padding: 0 .1em;
    }

    /* ── Pagination ──────────────────────────────────────────────────────────── */
    .pagination-bar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: .5rem;
      padding: .65rem 1.25rem;
      border-top: 1px solid var(--border);
    }
    .pagination-info {
      font-family: 'Share Tech Mono', monospace;
      font-size: .6rem;
      letter-spacing: .06em;
      color: var(--text-3);
    }
    .pagination-btns {
      display: flex;
      align-items: center;
      gap: .3rem;
      flex-wrap: wrap;
    }
    .page-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-width: 28px;
      height: 28px;
      padding: 0 .45rem;
      border: 1px solid var(--border-2);
      border-radius: 4px;
      background: rgba(255,255,255,.03);
      font-family: 'Share Tech Mono', monospace;
      font-size: .65rem;
      letter-spacing: .04em;
      color: var(--text-3);
      cursor: pointer;
      transition: border-color .15s, color .15s, background .15s;
    }
    .page-btn:hover:not(:disabled) {
      border-color: var(--green);
      color: var(--green);
      background: rgba(0,232,122,.05);
    }
    .page-btn.active {
      border-color: var(--green);
      color: var(--green);
      background: rgba(0,232,122,.08);
    }
    .page-btn:disabled { opacity: .3; cursor: not-allowed; }
    .page-ellipsis { border-color: transparent; background: transparent; cursor: default; pointer-events: none; }

    .feed-status {
      padding: 2.5rem 1.25rem;
      font-family: 'Share Tech Mono', monospace; font-size: .72rem;
      color: var(--text-3); text-align: center; letter-spacing: .06em;
    }
    .feed-status::before { content: '// '; opacity: .4; }
    .loading-row {
      display: flex; align-items: center; justify-content: center; gap: .6rem; padding: 2rem;
    }
    .big-spinner {
      width: 20px; height: 20px; border-radius: 50%;
      border: 2px solid var(--border-2); border-top-color: var(--green);
      animation: spin .7s linear infinite;
    }
    .loading-row span { font-family: 'Share Tech Mono', monospace; font-size: .7rem; color: var(--text-3); }
    .key-badge {
      display: inline-flex; align-items: center; gap: .35rem; padding: .25rem .6rem;
      background: rgba(0,232,122,.07); border: 1px solid rgba(0,232,122,.2); border-radius: 99px;
      font-family: 'Share Tech Mono', monospace; font-size: .6rem; color: var(--green);
    }
    .key-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); box-shadow: 0 0 5px var(--green); }
  </style>
</head>
<body>
<div class="page">

  <!-- Auth card -->
  <div class="card" id="auth-card">
    <div id="auth-screen">
      <div>
        <p class="auth-heading">Post Manager</p>
        <p class="auth-sub">Enter your API key to continue</p>
      </div>
      <div id="auth-alert"></div>
      <div class="field">
        <label for="key-input">API Key</label>
        <input type="password" id="key-input" placeholder="your-api-key" autocomplete="current-password" autofocus>
      </div>
      <div>
        <button class="btn btn-green" id="auth-btn" onclick="doAuth()">
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
          Unlock
        </button>
      </div>
    </div>
  </div>

  <!-- Main UI -->
  <div id="main-ui" style="display:none;flex-direction:column;gap:1.25rem;">

    <!-- Topbar -->
    <div class="card">
      <div class="topbar">
        <div class="topbar-brand">
          <span class="topbar-eyebrow">Updates</span>
          <span class="topbar-title"><?= htmlspecialchars(POST_PAGE_TITLE) ?></span>
        </div>
        <div class="topbar-actions">
          <span class="key-badge" id="key-indicator"><span class="key-dot"></span>authenticated</span>
          <button class="btn btn-ghost btn-sm" onclick="doLogout()">
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
            Sign out
          </button>
        </div>
      </div>
    </div>

    <!-- Compose -->
    <div class="card">
      <div class="section-head"><span class="section-title">New Post</span></div>
      <div id="compose-form">
        <div id="compose-alert"></div>

        <div class="field">
          <label for="c-text">Text <span style="color:var(--red)">*</span></label>
          <textarea id="c-text" placeholder="What's happening?" rows="3"></textarea>
        </div>

        <div class="field-row">
          <div class="field">
            <label for="c-tags">Tags</label>
            <input type="text" id="c-tags" placeholder="stream, update, blog">
            <span class="field-hint">comma-separated</span>
          </div>
          <div class="field">
            <label for="c-date">Date</label>
            <input type="text" id="c-date" placeholder="leave blank for now">
            <span class="field-hint">ISO-8601 or blank = now</span>
          </div>
        </div>

        <div class="field-row">
          <div class="field">
            <label for="c-link">Link URL</label>
            <input type="url" id="c-link" placeholder="https://…">
          </div>
          <div class="field">
            <label for="c-link-label">Link label</label>
            <input type="text" id="c-link-label" placeholder="Read more">
          </div>
        </div>

        <!-- Media upload -->
        <div class="field">
          <label>Media (images / video)</label>
          <div class="drop-zone" id="c-drop-zone">
            <input type="file" id="c-media-input" multiple
                   accept="image/jpeg,image/png,image/gif,image/webp,image/avif,image/svg+xml,video/mp4,video/quicktime,video/webm,video/ogg,video/x-msvideo,video/x-matroska">
            <div class="drop-zone-label">
              <svg class="drop-zone-icon" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
              <span class="drop-zone-text">Drop files here or click to browse</span>
              <span class="drop-zone-hint">JPEG · PNG · GIF · WEBP · MP4 · MOV · WEBM — up to 200 MB each</span>
            </div>
          </div>
          <div class="upload-progress" id="c-upload-progress">
            <div class="upload-progress-bar" id="c-progress-bar"></div>
          </div>
          <div class="media-previews" id="c-media-previews"></div>
        </div>

        <div class="compose-actions">
          <button class="btn btn-green" id="compose-btn" onclick="submitPost()">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
            Post update
          </button>
          <button class="btn btn-ghost btn-sm" onclick="clearCompose()">Clear</button>
        </div>
      </div>
    </div>

    <!-- Feed -->
    <div class="card">
      <div class="section-head">
        <span class="section-title">All Posts</span>
        <span class="section-badge" id="post-count">—</span>
      </div>

      <!-- Search bar -->
      <div class="search-wrap">
        <div class="search-input-wrap">
          <svg class="search-icon" width="14" height="14" viewBox="0 0 24 24" fill="none"
               stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
          </svg>
          <input class="search-input" type="text" id="search-input"
                 placeholder="Search posts by text, tag, or ID…"
                 autocomplete="off" spellcheck="false"
                 oninput="onSearchInput(this.value)">
          <button class="search-clear" id="search-clear"
                  onclick="clearSearch()" aria-label="Clear search">
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none"
                 stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </div>
      </div>

      <ul id="post-list">
        <li class="loading-row"><div class="big-spinner"></div><span>loading posts…</span></li>
      </ul>

      <!-- Pagination bar (built by JS) -->
      <div id="pagination-bar" style="display:none"></div>
    </div>

  </div><!-- /main-ui -->
</div><!-- /page -->

<script>
/* ════════════════════════════════════════════════════════════════════════════
   Post Manager — vanilla JS with media upload support
════════════════════════════════════════════════════════════════════════════ */
const API        = '<?= htmlspecialchars(POST_API_URL) ?>';
const UPLOAD_URL = '<?= htmlspecialchars(POST_UPLOAD_URL) ?>';
const STORE_KEY  = 'tc_post_mgr_key';

let apiKey = '';

/* ── SVG icons ──────────────────────────────────────────────────────────────── */
const ICO = {
  edit:   `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,
  trash:  `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>`,
  save:   `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>`,
  cancel: `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,
  warn:   `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`,
  ok:     `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`,
  play:   `<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>`,
};

/* ── Helpers ────────────────────────────────────────────────────────────────── */
function esc(str) {
  return String(str ?? '')
    .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
    .replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}

function showAlert(id, type, msg) {
  const el = document.getElementById(id);
  if (!el) return;
  el.innerHTML = `<div class="alert alert-${type}">${ICO[type==='error'?'warn':'ok']} ${esc(msg)}</div>`;
  setTimeout(() => { el.innerHTML = ''; }, 6000);
}

function setLoading(btnId, on) {
  const btn = document.getElementById(btnId);
  if (!btn) return;
  btn.disabled = on;
  const sp = btn.querySelector('.spinner');
  if (on && !sp) btn.insertAdjacentHTML('afterbegin', '<span class="spinner"></span>');
  if (!on && sp) sp.remove();
}

async function apiFetch(path, opts = {}) {
  const res = await fetch(API + (path ? '?' + path : ''), {
    ...opts,
    headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey, ...(opts.headers||{}) },
  });
  const json = await res.json();
  if (!json.ok) throw new Error(json.error || 'API error');
  return json.data;
}

function parseTags(str) { return str.split(',').map(t=>t.trim()).filter(Boolean); }

function relTime(iso) {
  if (!iso) return '';
  const d = Math.floor((Date.now() - new Date(iso)) / 1000);
  if (d < 60)     return 'just now';
  if (d < 3600)   return Math.floor(d/60)   + 'm ago';
  if (d < 86400)  return Math.floor(d/3600)  + 'h ago';
  if (d < 604800) return Math.floor(d/86400) + 'd ago';
  return new Date(iso).toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'});
}

/* ════════════════════════════════════════════════════════════════════════════
   MEDIA UPLOAD SYSTEM
   Each compose / edit form has its own pending media array.
   Uploads happen immediately on file selection; results stored in pendingMedia[formId].
════════════════════════════════════════════════════════════════════════════ */
const pendingMedia = {};   // formId → [{url, type, mime, filename}, …]

function initDropZone(formId, dropZoneId, inputId, progressId, barId, previewsId) {
  pendingMedia[formId] = pendingMedia[formId] || [];

  const zone     = document.getElementById(dropZoneId);
  const input    = document.getElementById(inputId);
  const progress = document.getElementById(progressId);
  const bar      = document.getElementById(barId);
  const previews = document.getElementById(previewsId);

  if (!zone) return;

  // Drag-over highlight
  zone.addEventListener('dragover', e => { e.preventDefault(); zone.classList.add('drag-over'); });
  zone.addEventListener('dragleave', () => zone.classList.remove('drag-over'));
  zone.addEventListener('drop', e => {
    e.preventDefault();
    zone.classList.remove('drag-over');
    uploadFiles(formId, e.dataTransfer.files, progress, bar, previews);
  });

  input.addEventListener('change', () => {
    uploadFiles(formId, input.files, progress, bar, previews);
    input.value = '';   // allow re-selecting the same file
  });
}

async function uploadFiles(formId, fileList, progress, bar, previews) {
  for (const file of fileList) {
    progress.classList.add('visible');
    bar.style.width = '0%';

    try {
      const fd = new FormData();
      fd.append('file', file);

      // XHR for progress events
      const result = await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', UPLOAD_URL);
        xhr.setRequestHeader('X-API-Key', apiKey);
        xhr.upload.addEventListener('progress', e => {
          if (e.lengthComputable) bar.style.width = Math.round(e.loaded / e.total * 100) + '%';
        });
        xhr.addEventListener('load', () => {
          try { resolve(JSON.parse(xhr.responseText)); }
          catch(e) { reject(new Error('Bad response')); }
        });
        xhr.addEventListener('error', () => reject(new Error('Network error')));
        xhr.send(fd);
      });

      if (!result.ok) throw new Error(result.error || 'Upload failed');

      bar.style.width = '100%';
      pendingMedia[formId].push({ url: result.url, type: result.type, mime: result.mime, filename: result.filename });
      addPreviewThumb(formId, result, previews);

    } catch (err) {
      alert('Upload failed: ' + err.message);
    } finally {
      setTimeout(() => { progress.classList.remove('visible'); bar.style.width = '0%'; }, 600);
    }
  }
}

function addPreviewThumb(formId, item, previewsEl) {
  const idx  = pendingMedia[formId].length - 1;
  const wrap = document.createElement('div');
  wrap.className = 'media-preview-item';
  wrap.dataset.idx = idx;

  if (item.type === 'image') {
    wrap.innerHTML = `<img src="${esc(item.url)}" alt="">
      <span class="media-preview-type">img</span>
      <button class="media-remove" title="Remove" onclick="removeMedia('${formId}',${idx})">✕</button>`;
  } else {
    wrap.innerHTML = `<div style="width:100%;height:100%;background:var(--surface-2);display:flex;align-items:center;justify-content:center;">
        <svg width="22" height="22" viewBox="0 0 24 24" fill="var(--green)"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </div>
      <span class="media-preview-type">vid</span>
      <button class="media-remove" title="Remove" onclick="removeMedia('${formId}',${idx})">✕</button>`;
  }
  previewsEl.appendChild(wrap);
}

function removeMedia(formId, idx) {
  pendingMedia[formId][idx] = null;   // null = removed (preserve index alignment)
  const previews = document.querySelectorAll(`[data-idx="${idx}"]`);
  previews.forEach(el => el.remove());
}

function getMedia(formId) {
  return (pendingMedia[formId] || []).filter(Boolean);
}

function clearMedia(formId, previewsId) {
  pendingMedia[formId] = [];
  const el = document.getElementById(previewsId);
  if (el) el.innerHTML = '';
}

/* ── Render media strip in admin feed ───────────────────────────────────────── */
function mediaStripHTML(mediaArr) {
  if (!mediaArr || !mediaArr.length) return '';
  const items = mediaArr.map(m => {
    if (m.type === 'image') {
      return `<div class="post-media-thumb"><img src="${esc(m.url)}" alt="" loading="lazy"><span class="media-thumb-label">img</span></div>`;
    }
    return `<div class="post-media-thumb">
      <div class="vid-icon">${ICO.play}</div>
      <span class="media-thumb-label">vid</span>
    </div>`;
  }).join('');
  return `<div class="post-media-strip">${items}</div>`;
}

/* ── Auth ────────────────────────────────────────────────────────────────────── */
function doAuth() {
  const key = document.getElementById('key-input').value.trim();
  if (!key) { showAlert('auth-alert','error','Enter your API key.'); return; }
  apiKey = key;
  setLoading('auth-btn', true);
  apiFetch('')
    .then(() => {
      sessionStorage.setItem(STORE_KEY, key);
      document.getElementById('auth-card').style.display = 'none';
      document.getElementById('main-ui').style.display = 'flex';
      initDropZone('compose', 'c-drop-zone', 'c-media-input', 'c-upload-progress', 'c-progress-bar', 'c-media-previews');
      loadFeed();
    })
    .catch(err => {
      apiKey = '';
      setLoading('auth-btn', false);
      showAlert('auth-alert','error','Invalid key: ' + err.message);
    });
}

function doLogout() { sessionStorage.removeItem(STORE_KEY); location.reload(); }

/* ════════════════════════════════════════════════════════════════════════════
   FEED — search, pagination, rendering
════════════════════════════════════════════════════════════════════════════ */
const PER_PAGE = 15;               // posts shown per page
let allPosts   = [];               // master array from last API fetch
let currentPage = 1;
let searchQuery = '';
let searchTimer = null;

/* Load all posts from API, then filter+render */
function loadFeed() {
  const list = document.getElementById('post-list');
  list.innerHTML = `<li class="loading-row"><div class="big-spinner"></div><span>loading…</span></li>`;
  apiFetch('limit=1000')           // fetch everything; client handles paging
    .then(data => {
      allPosts = data.items || [];
      currentPage = 1;
      filterAndPage();
    })
    .catch(err => {
      list.innerHTML = `<li class="feed-status">failed: ${esc(err.message)}</li>`;
      hidePagination();
    });
}

/* Filter allPosts by searchQuery, then render the requested page */
function filterAndPage() {
  const q = searchQuery.trim().toLowerCase();

  const filtered = q
    ? allPosts.filter(p =>
        (p.text  || '').toLowerCase().includes(q) ||
        (p.id    || '').toLowerCase().includes(q) ||
        (p.tags  || []).some(t => t.toLowerCase().includes(q)) ||
        (p.link  || '').toLowerCase().includes(q) ||
        (p.note  || '').toLowerCase().includes(q)
      )
    : allPosts;

  const total      = filtered.length;
  const totalPages = Math.max(1, Math.ceil(total / PER_PAGE));
  currentPage      = Math.min(currentPage, totalPages);
  const start      = (currentPage - 1) * PER_PAGE;
  const page       = filtered.slice(start, start + PER_PAGE);

  // Update badge
  const badge = document.getElementById('post-count');
  if (q) {
    badge.textContent = filtered.length + ' of ' + allPosts.length + ' match' + (filtered.length !== 1 ? 'es' : '');
  } else {
    badge.textContent = allPosts.length + ' post' + (allPosts.length !== 1 ? 's' : '');
  }

  // Render list
  const list = document.getElementById('post-list');
  if (!page.length) {
    list.innerHTML = q
      ? `<li class="feed-status">no results for "${esc(q)}"</li>`
      : '<li class="feed-status">no posts yet</li>';
    hidePagination();
    return;
  }
  list.innerHTML = page.map(p => postHTML(p, q)).join('');

  // Render pagination
  if (totalPages <= 1) {
    hidePagination();
  } else {
    renderPagination(currentPage, totalPages, total, start);
  }
}

/* ── Pagination controls ─────────────────────────────────────────────────── */
function renderPagination(page, totalPages, total, start) {
  const bar   = document.getElementById('pagination-bar');
  bar.style.display = '';
  bar.className = 'pagination-bar';

  const from = start + 1;
  const to   = Math.min(start + PER_PAGE, total);

  // Build page number buttons with sliding window ± 2
  let btns = '';
  const lo = Math.max(1, page - 2);
  const hi = Math.min(totalPages, page + 2);

  btns += `<button class="page-btn" ${page <= 1 ? 'disabled' : ''}
             onclick="goPage(${page - 1})">‹</button>`;

  if (lo > 1) {
    btns += `<button class="page-btn" onclick="goPage(1)">1</button>`;
    if (lo > 2) btns += `<span class="page-btn page-ellipsis">…</span>`;
  }
  for (let p = lo; p <= hi; p++) {
    btns += `<button class="page-btn${p === page ? ' active' : ''}"
               onclick="goPage(${p})">${p}</button>`;
  }
  if (hi < totalPages) {
    if (hi < totalPages - 1) btns += `<span class="page-btn page-ellipsis">…</span>`;
    btns += `<button class="page-btn" onclick="goPage(${totalPages})">${totalPages}</button>`;
  }
  btns += `<button class="page-btn" ${page >= totalPages ? 'disabled' : ''}
             onclick="goPage(${page + 1})">›</button>`;

  bar.innerHTML = `
    <span class="pagination-info">${from}–${to} of ${total}</span>
    <div class="pagination-btns">${btns}</div>`;
}

function hidePagination() {
  const bar = document.getElementById('pagination-bar');
  bar.style.display = 'none';
  bar.innerHTML = '';
}

function goPage(n) {
  currentPage = n;
  filterAndPage();
  // Scroll the list back into view smoothly
  document.getElementById('post-list').scrollIntoView({ behavior: 'smooth', block: 'start' });
}

/* ── Search input handler (debounced 250 ms) ─────────────────────────────── */
function onSearchInput(val) {
  const clearBtn = document.getElementById('search-clear');
  clearBtn.classList.toggle('visible', val.length > 0);
  clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    searchQuery = val;
    currentPage = 1;
    filterAndPage();
  }, 250);
}

function clearSearch() {
  document.getElementById('search-input').value = '';
  document.getElementById('search-clear').classList.remove('visible');
  searchQuery = '';
  currentPage = 1;
  filterAndPage();
  document.getElementById('search-input').focus();
}

/* ── Highlight matched text ──────────────────────────────────────────────── */
function highlight(text, query) {
  if (!query) return esc(text);
  var safe  = esc(text);
  // Escape regex special chars safely without regex literal char-class issues
  var safeQ = esc(query).replace(/[-[\]{}()*+?.,^$|#\s]/g, '\\$&');
  try {
    return safe.replace(new RegExp('(' + safeQ + ')', 'gi'), '<mark>$1</mark>');
  } catch(e) { return safe; }
}

function renderFeed(posts, total) {
  // Legacy path — called by old code paths if any; route through new system
  allPosts = posts;
  filterAndPage();
}

function postHTML(p, query) {
  query = query || '';
  const tags  = (p.tags||[]).map(t=>`<span class="post-tag">${highlight(t, query)}</span>`).join('');
  const link  = p.link ? `<a class="post-link" href="${esc(p.link)}" target="_blank" rel="noopener">${esc(p.link_label||p.link)}</a>` : '';
  const media = mediaStripHTML(p.media||[]);

  return `<li class="post-item" id="pi-${esc(p.id)}">
    <div class="post-meta">
      <span class="post-id">#${highlight(p.id, query)}</span>
      <span class="post-date">${esc(relTime(p.date))}</span>
      ${tags}
    </div>
    <p class="post-text">${highlight(p.text, query)}</p>
    ${link}
    ${media}
    <div class="post-actions">
      <button class="btn btn-ghost btn-sm" onclick="toggleEdit('${esc(p.id)}')">${ICO.edit} Edit</button>
      <button class="btn btn-red   btn-sm" onclick="deletePost('${esc(p.id)}')">${ICO.trash} Delete</button>
    </div>
    <div class="edit-form" id="ef-${esc(p.id)}">
      <div class="field">
        <label>Text</label>
        <textarea id="ef-text-${esc(p.id)}" rows="3">${esc(p.text)}</textarea>
      </div>
      <div class="field-row">
        <div class="field">
          <label>Tags</label>
          <input type="text" id="ef-tags-${esc(p.id)}" value="${esc((p.tags||[]).join(', '))}">
          <span class="field-hint">comma-separated</span>
        </div>
        <div class="field">
          <label>Date (ISO-8601)</label>
          <input type="text" id="ef-date-${esc(p.id)}" value="${esc(p.date||'')}">
        </div>
      </div>
      <div class="field-row">
        <div class="field">
          <label>Link URL</label>
          <input type="url" id="ef-link-${esc(p.id)}" value="${esc(p.link||'')}">
        </div>
        <div class="field">
          <label>Link label</label>
          <input type="text" id="ef-linklabel-${esc(p.id)}" value="${esc(p.link_label||'')}">
        </div>
      </div>
      <!-- Edit: add more media (cannot remove existing via UI — use api.php directly) -->
      <div class="field">
        <label>Add more media</label>
        <div class="drop-zone" id="ef-drop-${esc(p.id)}">
          <input type="file" id="ef-media-input-${esc(p.id)}" multiple
                 accept="image/jpeg,image/png,image/gif,image/webp,image/avif,image/svg+xml,video/mp4,video/quicktime,video/webm,video/ogg,video/x-msvideo,video/x-matroska">
          <div class="drop-zone-label">
            <span class="drop-zone-text">Drop or click to add media</span>
          </div>
        </div>
        <div class="upload-progress" id="ef-progress-${esc(p.id)}">
          <div class="upload-progress-bar" id="ef-bar-${esc(p.id)}"></div>
        </div>
        <div class="media-previews" id="ef-previews-${esc(p.id)}"></div>
      </div>
      <div style="display:flex;gap:.5rem;flex-wrap:wrap;">
        <button class="btn btn-blue btn-sm" onclick="saveEdit('${esc(p.id)}')">${ICO.save} Save</button>
        <button class="btn btn-ghost btn-sm" onclick="toggleEdit('${esc(p.id)}')">${ICO.cancel} Cancel</button>
      </div>
      <div id="ef-alert-${esc(p.id)}"></div>
    </div>
  </li>`;
}

/* ── Compose ─────────────────────────────────────────────────────────────────── */
function clearCompose() {
  ['c-text','c-tags','c-date','c-link','c-link-label'].forEach(id => {
    document.getElementById(id).value = '';
  });
  clearMedia('compose', 'c-media-previews');
}

function submitPost() {
  const text = document.getElementById('c-text').value.trim();
  if (!text) { showAlert('compose-alert','error','Text is required.'); return; }

  const body = {
    text,
    tags:       parseTags(document.getElementById('c-tags').value),
    link:       document.getElementById('c-link').value.trim(),
    link_label: document.getElementById('c-link-label').value.trim(),
    media:      getMedia('compose'),
  };
  const date = document.getElementById('c-date').value.trim();
  if (date) body.date = date;

  setLoading('compose-btn', true);
  apiFetch('', { method: 'POST', body: JSON.stringify(body) })
    .then(post => {
      clearCompose();
      setLoading('compose-btn', false);
      showAlert('compose-alert','success','Posted: #' + post.id);
      loadFeed();
    })
    .catch(err => { setLoading('compose-btn', false); showAlert('compose-alert','error',err.message); });
}

/* ── Edit ────────────────────────────────────────────────────────────────────── */
function toggleEdit(id) {
  const ef = document.getElementById('ef-' + id);
  ef.classList.toggle('open');
  if (ef.classList.contains('open')) {
    // Init drop zone for this edit form
    initDropZone('ef-'+id, 'ef-drop-'+id, 'ef-media-input-'+id,
                 'ef-progress-'+id, 'ef-bar-'+id, 'ef-previews-'+id);
  }
}

function saveEdit(id) {
  const text = document.getElementById('ef-text-' + id).value.trim();
  if (!text) { showAlert('ef-alert-'+id,'error','Text cannot be empty.'); return; }

  // Fetch current post to merge existing media
  apiFetch('id=' + encodeURIComponent(id))
    .then(current => {
      const existingMedia = current.media || [];
      const newMedia      = getMedia('ef-' + id);
      const body = {
        text,
        tags:       parseTags(document.getElementById('ef-tags-' + id).value),
        link:       document.getElementById('ef-link-' + id).value.trim(),
        link_label: document.getElementById('ef-linklabel-' + id).value.trim(),
        media:      [...existingMedia, ...newMedia],
      };
      const date = document.getElementById('ef-date-' + id).value.trim();
      if (date) body.date = date;

      return apiFetch('id=' + encodeURIComponent(id), { method: 'PUT', body: JSON.stringify(body) });
    })
    .then(updated => {
      document.getElementById('pi-' + id).outerHTML = postHTML(updated, searchQuery);
    })
    .catch(err => showAlert('ef-alert-'+id,'error',err.message));
}

/* ── Delete ──────────────────────────────────────────────────────────────────── */
function deletePost(id) {
  if (!confirm('Delete post #' + id + '? This cannot be undone.')) return;
  apiFetch('id=' + encodeURIComponent(id), { method: 'DELETE' })
    .then(() => {
      const li = document.getElementById('pi-' + id);
      li.style.transition = 'opacity .2s, transform .2s';
      li.style.opacity = '0'; li.style.transform = 'translateX(10px)';
      setTimeout(() => { li.remove(); loadFeed(); }, 220);
    })
    .catch(err => alert('Delete failed: ' + err.message));
}

/* ── Enter key + session restore ─────────────────────────────────────────────── */
document.getElementById('key-input').addEventListener('keydown', e => { if (e.key==='Enter') doAuth(); });
/* Search: clear on Escape */
document.addEventListener('keydown', e => {
  if (e.key === 'Escape' && searchQuery) clearSearch();
});

(function() {
  const saved = sessionStorage.getItem(STORE_KEY);
  if (!saved) return;
  document.getElementById('key-input').value = saved;
  apiKey = saved;
  apiFetch('')
    .then(() => {
      document.getElementById('auth-card').style.display = 'none';
      document.getElementById('main-ui').style.display = 'flex';
      initDropZone('compose','c-drop-zone','c-media-input','c-upload-progress','c-progress-bar','c-media-previews');
      loadFeed();
    })
    .catch(() => { sessionStorage.removeItem(STORE_KEY); apiKey = ''; });
})();
</script>
</body>
</html>
