// GCalendar.jsx — agenda no estilo Google Agenda (dia, semana, mes e lista).
// Componente generico e sem dependencias do resto do app: recebe eventos
// { id, date:'YYYY-MM-DD', time:'HH:MM' | null (dia inteiro), duration (min),
//   title, subtitle, color, category } e avisa o pai por callbacks.
// O mesmo arquivo e usado na Demo Servicos e na Demo Comercial.
'use strict';

const GC_MONTHS = ['janeiro','fevereiro','março','abril','maio','junho','julho','agosto','setembro','outubro','novembro','dezembro'];
const GC_MONTHS_SHORT = ['jan','fev','mar','abr','mai','jun','jul','ago','set','out','nov','dez'];
const GC_WEEK = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'];
const GC_WEEK_LONG = ['Domingo','Segunda','Terça','Quarta','Quinta','Sexta','Sábado'];
const GC_WEEK_1 = ['D','S','T','Q','Q','S','S'];
const GC_HOUR_PX = 52;
const GC_ALLDAY_MAX = 2;
const GC_MOBILE_QUERY = '(max-width: 767px)';

const gcIso = (d) => new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().slice(0, 10);
const gcAddDays = (d, n) => { const x = new Date(d.getFullYear(), d.getMonth(), d.getDate()); x.setDate(x.getDate() + n); return x; };
const gcStartOfWeek = (d) => gcAddDays(d, -d.getDay());
const gcMin = (t) => { const [h, m] = t.split(':').map(Number); return h * 60 + m; };
const gcHHMM = (min) => `${String(Math.floor(min / 60)).padStart(2, '0')}:${String(min % 60).padStart(2, '0')}`;
const gcDur = (e) => e.duration || 60;
const gcRange = (e) => (e.time ? `${e.time} – ${gcHHMM(gcMin(e.time) + gcDur(e))}` : 'Dia inteiro');
const gcCap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
const gcSortEvents = (a, b) => (a.time ? 1 : 0) - (b.time ? 1 : 0) || (a.time || '').localeCompare(b.time || '');

function useGcIsMobile() {
  const [isMobile, setIsMobile] = React.useState(() => window.matchMedia(GC_MOBILE_QUERY).matches);
  React.useEffect(() => {
    const mq = window.matchMedia(GC_MOBILE_QUERY);
    const onChange = () => setIsMobile(mq.matches);
    mq.addEventListener('change', onChange);
    return () => mq.removeEventListener('change', onChange);
  }, []);
  return isMobile;
}

// relogio que atualiza a cada minuto (linha vermelha de "agora")
function useGcNow() {
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 60000);
    return () => clearInterval(id);
  }, []);
  return now;
}

// Eventos que se cruzam no tempo dividem a largura da coluna, como no Google.
function gcLayoutDay(events) {
  const sorted = [...events].sort((a, b) => gcMin(a.time) - gcMin(b.time) || gcDur(b) - gcDur(a));
  const placed = [];
  let cluster = [];
  let clusterEnd = -1;
  const flush = () => {
    const colEnds = [];
    const local = cluster.map((ev) => {
      const start = gcMin(ev.time);
      let col = colEnds.findIndex((end) => end <= start);
      if (col === -1) { col = colEnds.length; colEnds.push(0); }
      colEnds[col] = start + gcDur(ev);
      return { ev, col };
    });
    local.forEach((p) => placed.push({ ...p, cols: colEnds.length }));
    cluster = [];
    clusterEnd = -1;
  };
  sorted.forEach((ev) => {
    const start = gcMin(ev.time);
    if (cluster.length && start >= clusterEnd) flush();
    cluster.push(ev);
    clusterEnd = Math.max(clusterEnd, start + gcDur(ev));
  });
  if (cluster.length) flush();
  return placed;
}

function GcIcon({ d, size = 18, color = 'currentColor', sw = 2 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <path d={d} />
    </svg>
  );
}
const GC_I = { left: 'M15 18l-6-6 6-6', right: 'M9 18l6-6-6-6', plus: 'M12 5v14M5 12h14', check: 'M5 12.5l4.5 4.5L19 7.5' };

// ── Mini calendario da lateral ────────────────────────────────────────────
function GcMiniMonth({ selected, onPick, markedDates, accent, todayIso }) {
  const [view, setView] = React.useState(() => new Date(selected.getFullYear(), selected.getMonth(), 1));
  React.useEffect(() => { setView(new Date(selected.getFullYear(), selected.getMonth(), 1)); }, [selected.getFullYear(), selected.getMonth()]);
  const start = gcStartOfWeek(view);
  const cells = Array.from({ length: 42 }, (_, i) => gcAddDays(start, i));
  const selIso = gcIso(selected);
  const shift = (n) => setView(new Date(view.getFullYear(), view.getMonth() + n, 1));
  return (
    <div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-3">
      <div className="flex items-center justify-between mb-1 px-1">
        <span className="text-sm font-semibold text-slate-700">{gcCap(GC_MONTHS[view.getMonth()])} {view.getFullYear()}</span>
        <div className="flex">
          <button onClick={() => shift(-1)} aria-label="Mês anterior" className="w-7 h-7 rounded-full flex items-center justify-center text-slate-500 hover:bg-slate-100"><GcIcon d={GC_I.left} size={16} /></button>
          <button onClick={() => shift(1)} aria-label="Próximo mês" className="w-7 h-7 rounded-full flex items-center justify-center text-slate-500 hover:bg-slate-100"><GcIcon d={GC_I.right} size={16} /></button>
        </div>
      </div>
      <div className="grid grid-cols-7 text-center">
        {GC_WEEK_1.map((w, i) => <span key={i} className="text-[10px] font-semibold text-slate-400 py-1">{w}</span>)}
        {cells.map((d) => {
          const iso = gcIso(d);
          const out = d.getMonth() !== view.getMonth();
          const isToday = iso === todayIso;
          const isSel = iso === selIso && !isToday;
          return (
            <button key={iso} onClick={() => onPick(d)}
              className={`relative mx-auto w-7 h-7 rounded-full text-[11px] font-medium flex items-center justify-center transition-colors ${isToday ? 'text-white' : isSel ? 'bg-blue-100 text-blue-700' : out ? 'text-slate-300 hover:bg-slate-100' : 'text-slate-600 hover:bg-slate-100'}`}
              style={isToday ? { background: accent } : undefined}>
              {d.getDate()}
              {markedDates.has(iso) && !isToday && <span className="absolute bottom-0.5 w-1 h-1 rounded-full" style={{ background: accent }} />}
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ── Chip de evento (mes / dia inteiro) ────────────────────────────────────
function GcChip({ ev, onClick, solid }) {
  const allDay = !ev.time || solid;
  return (
    <button onClick={(e) => { e.stopPropagation(); onClick(ev); }} title={`${gcRange(ev)} · ${ev.title}`}
      className="w-full flex items-center gap-1.5 px-1.5 py-[3px] rounded-md text-[11.5px] leading-tight text-left truncate transition-[filter] hover:brightness-95 hover:bg-slate-100"
      style={allDay ? { background: ev.color, color: '#fff' } : { color: '#334155' }}>
      {!allDay && <span className="w-2 h-2 rounded-full flex-shrink-0" style={{ background: ev.color }} />}
      {!allDay && <span className="text-slate-500 flex-shrink-0">{ev.time}</span>}
      <span className="truncate font-medium">{ev.title}</span>
    </button>
  );
}

// ── Visao mes ─────────────────────────────────────────────────────────────
function GcMonthView({ cursor, byDate, todayIso, accent, isMobile, onOpenDay, onCreate, onEvent }) {
  const start = gcStartOfWeek(new Date(cursor.getFullYear(), cursor.getMonth(), 1));
  const cells = Array.from({ length: 42 }, (_, i) => gcAddDays(start, i));
  const maxChips = 3;
  return (
    <div className="flex flex-col">
      <div className="grid grid-cols-7 border-b border-slate-100">
        {GC_WEEK.map((w, i) => (
          <div key={w} className="text-center text-[11px] font-semibold uppercase tracking-wide text-slate-400 py-2">{isMobile ? GC_WEEK_1[i] : w}</div>
        ))}
      </div>
      <div className="grid grid-cols-7">
        {cells.map((d, i) => {
          const iso = gcIso(d);
          const evs = byDate[iso] || [];
          const out = d.getMonth() !== cursor.getMonth();
          const isToday = iso === todayIso;
          return (
            <div key={iso}
              onClick={() => (isMobile ? onOpenDay(d) : onCreate({ date: iso, time: null }))}
              className={`relative border-slate-100 ${i % 7 ? 'border-l' : ''} ${i >= 7 ? 'border-t' : ''} ${out ? 'bg-slate-50/70' : 'bg-white'} cursor-pointer hover:bg-slate-50 transition-colors min-w-0`}
              style={{ minHeight: isMobile ? 64 : 118 }}>
              <div className="flex justify-center pt-1.5 pb-1">
                <button onClick={(e) => { e.stopPropagation(); onOpenDay(d); }}
                  className={`min-w-7 h-7 px-1.5 rounded-full text-xs font-semibold flex items-center justify-center transition-colors ${isToday ? 'text-white' : out ? 'text-slate-300 hover:bg-slate-200' : 'text-slate-600 hover:bg-slate-200'}`}
                  style={isToday ? { background: accent } : undefined}>
                  {d.getDate() === 1 && !isMobile ? `${d.getDate()} ${GC_MONTHS_SHORT[d.getMonth()]}` : d.getDate()}
                </button>
              </div>
              {isMobile ? (
                <div className="flex flex-col gap-[3px] px-1 pb-1">
                  {evs.slice(0, 3).map((ev) => <span key={ev.id} className="h-[5px] rounded-full" style={{ background: ev.color }} />)}
                  {evs.length > 3 && <span className="text-[9px] leading-none text-slate-400 text-center">+{evs.length - 3}</span>}
                </div>
              ) : (
                <div className="flex flex-col gap-[2px] px-1 pb-1.5">
                  {evs.slice(0, maxChips).map((ev) => <GcChip key={ev.id} ev={ev} onClick={onEvent} />)}
                  {evs.length > maxChips && (
                    <button onClick={(e) => { e.stopPropagation(); onOpenDay(d); }}
                      className="text-left text-[11.5px] font-semibold text-slate-500 hover:bg-slate-200 rounded-md px-1.5 py-[2px]">
                      +{evs.length - maxChips} mais
                    </button>
                  )}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Visao dia / 3 dias / semana (grade de horarios) ───────────────────────
function GcTimeGrid({ days, byDate, todayIso, accent, now, dayStart, dayEnd, isMobile, onOpenDay, onCreate, onEvent }) {
  const scrollRef = React.useRef(null);
  const hours = Array.from({ length: dayEnd - dayStart }, (_, i) => dayStart + i);
  const gridH = hours.length * GC_HOUR_PX;
  const nowMin = now.getHours() * 60 + now.getMinutes();
  const showNow = nowMin >= dayStart * 60 && nowMin <= dayEnd * 60;
  const allDayRows = days.map((d) => (byDate[gcIso(d)] || []).filter((e) => !e.time));
  const hasAllDay = allDayRows.some((r) => r.length);
  const cols = `${isMobile ? 44 : 56}px repeat(${days.length}, minmax(0, 1fr))`;

  // abre a grade perto da hora atual (ou no inicio do expediente)
  React.useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const target = showNow ? nowMin / 60 - dayStart - 1.5 : 1;
    el.scrollTop = Math.max(0, target * GC_HOUR_PX);
  }, [days.length, gcIso(days[0])]);

  const onColumnClick = (e, d) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const min = dayStart * 60 + Math.floor(((e.clientY - rect.top) / GC_HOUR_PX) * 2) * 30;
    onCreate({ date: gcIso(d), time: gcHHMM(Math.min(min, dayEnd * 60 - 30)) });
  };

  return (
    <div className="flex flex-col">
      {/* cabecalho dos dias */}
      <div className="grid border-b border-slate-100" style={{ gridTemplateColumns: cols }}>
        <div className="flex items-end justify-end pr-2 pb-1 text-[10px] text-slate-400">GMT-3</div>
        {days.map((d) => {
          const iso = gcIso(d);
          const isToday = iso === todayIso;
          return (
            <button key={iso} onClick={() => onOpenDay(d)} className="flex flex-col items-center py-2 group min-w-0">
              <span className="text-[11px] font-semibold uppercase tracking-wide" style={{ color: isToday ? accent : '#94A3B8' }}>{GC_WEEK[d.getDay()]}</span>
              <span className={`mt-0.5 w-10 h-10 rounded-full flex items-center justify-center text-xl font-semibold transition-colors ${isToday ? 'text-white' : 'text-slate-700 group-hover:bg-slate-100'}`}
                style={isToday ? { background: accent } : undefined}>{d.getDate()}</span>
            </button>
          );
        })}
      </div>

      {/* linha de "dia inteiro" */}
      {hasAllDay && (
        <div className="grid border-b border-slate-100" style={{ gridTemplateColumns: cols }}>
          <div className="text-[10px] text-slate-400 text-right pr-2 pt-1.5">dia todo</div>
          {allDayRows.map((row, i) => (
            <div key={i} className="border-l border-slate-100 p-1 flex flex-col gap-[2px] min-w-0">
              {row.slice(0, GC_ALLDAY_MAX).map((ev) => <GcChip key={ev.id} ev={ev} onClick={onEvent} solid />)}
              {row.length > GC_ALLDAY_MAX && (
                <button onClick={() => onOpenDay(days[i])} className="text-left text-[11px] font-semibold text-slate-500 hover:bg-slate-100 rounded-md px-1.5 py-[1px]">
                  +{row.length - GC_ALLDAY_MAX} mais
                </button>
              )}
            </div>
          ))}
        </div>
      )}

      {/* grade de horarios */}
      <div ref={scrollRef} className="overflow-y-auto gc-scroll" style={{ height: isMobile ? 'calc(100dvh - 330px)' : 'calc(100dvh - 300px)', minHeight: 380 }}>
        <div className="grid relative" style={{ gridTemplateColumns: cols, height: gridH }}>
          <div className="relative">
            {hours.map((h, i) => (
              <span key={h} className="absolute right-2 -translate-y-1/2 text-[10.5px] text-slate-400" style={{ top: i * GC_HOUR_PX }}>
                {i === 0 ? '' : `${String(h).padStart(2, '0')}:00`}
              </span>
            ))}
          </div>
          {days.map((d) => {
            const iso = gcIso(d);
            const isToday = iso === todayIso;
            const timed = (byDate[iso] || []).filter((e) => e.time);
            return (
              <div key={iso} onClick={(e) => onColumnClick(e, d)}
                className={`relative border-l border-slate-100 cursor-pointer min-w-0 ${isToday ? 'bg-orange-50/30' : ''}`}
                style={{ backgroundImage: `repeating-linear-gradient(to bottom, #EEF2F6 0, #EEF2F6 1px, transparent 1px, transparent ${GC_HOUR_PX}px)` }}>
                {gcLayoutDay(timed).map(({ ev, col, cols: n }) => {
                  const startMin = Math.max(gcMin(ev.time), dayStart * 60);
                  const top = ((startMin - dayStart * 60) / 60) * GC_HOUR_PX;
                  const height = Math.max((gcDur(ev) / 60) * GC_HOUR_PX - 3, 22);
                  const roomy = height >= 44;
                  return (
                    <button key={ev.id} onClick={(e) => { e.stopPropagation(); onEvent(ev); }}
                      className="absolute rounded-lg text-left overflow-hidden px-1.5 py-1 shadow-sm hover:shadow-md transition-shadow ring-1 ring-white"
                      style={{ top: top + 1, height, left: `calc(${(col / n) * 100}% + 2px)`, width: `calc(${100 / n}% - 5px)`, background: ev.color, color: '#fff', zIndex: 10 + col }}>
                      {roomy ? (
                        <>
                          <p className="text-[11.5px] font-semibold leading-tight truncate">{ev.title}</p>
                          <p className="text-[10.5px] leading-tight opacity-90 truncate">{gcRange(ev)}</p>
                          {height >= 64 && ev.subtitle && <p className="text-[10.5px] leading-tight opacity-80 truncate mt-0.5">{ev.subtitle}</p>}
                        </>
                      ) : (
                        <p className="text-[11px] leading-tight truncate -mt-px"><span className="font-semibold">{ev.title}</span><span className="opacity-90">, {ev.time}</span></p>
                      )}
                    </button>
                  );
                })}
                {isToday && showNow && (
                  <div className="absolute left-0 right-0 pointer-events-none z-30" style={{ top: ((nowMin - dayStart * 60) / 60) * GC_HOUR_PX }}>
                    <div className="relative h-[2px] bg-red-500">
                      <span className="absolute -left-1.5 -top-[5px] w-3 h-3 rounded-full bg-red-500" />
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Visao lista (programacao) ─────────────────────────────────────────────
function GcListView({ cursor, byDate, todayIso, accent, onEvent, onOpenDay }) {
  const days = Array.from({ length: 30 }, (_, i) => gcAddDays(cursor, i)).filter((d) => (byDate[gcIso(d)] || []).length);
  if (!days.length) {
    return <div className="py-20 text-center text-sm text-slate-400">Nada agendado nos próximos 30 dias.</div>;
  }
  return (
    <div className="divide-y divide-slate-100">
      {days.map((d) => {
        const iso = gcIso(d);
        const isToday = iso === todayIso;
        return (
          <div key={iso} className="flex gap-3 sm:gap-6 px-3 sm:px-5 py-3">
            <button onClick={() => onOpenDay(d)} className="w-12 sm:w-28 flex-shrink-0 flex flex-col sm:flex-row items-center sm:items-start sm:gap-2 text-left">
              <span className={`w-9 h-9 rounded-full flex items-center justify-center text-lg font-semibold ${isToday ? 'text-white' : 'text-slate-700'}`} style={isToday ? { background: accent } : undefined}>{d.getDate()}</span>
              <span className="text-[10.5px] sm:text-[11px] sm:pt-2.5 font-semibold uppercase text-slate-400 text-center">{GC_MONTHS_SHORT[d.getMonth()]}, {GC_WEEK[d.getDay()]}</span>
            </button>
            <div className="flex-1 min-w-0 flex flex-col gap-1">
              {byDate[iso].map((ev) => (
                <button key={ev.id} onClick={() => onEvent(ev)} className="flex items-start sm:items-center gap-3 text-left rounded-xl px-2 py-2 hover:bg-slate-50 transition-colors min-w-0">
                  <span className="w-2.5 h-2.5 mt-1 sm:mt-0 rounded-full flex-shrink-0" style={{ background: ev.color }} />
                  <span className="w-28 flex-shrink-0 text-xs text-slate-500 hidden sm:block">{gcRange(ev)}</span>
                  <span className="min-w-0">
                    <span className="block text-sm font-semibold text-slate-700 truncate">{ev.title}</span>
                    <span className="block text-xs text-slate-400 truncate"><span className="sm:hidden">{gcRange(ev)}{ev.subtitle ? ' · ' : ''}</span>{ev.subtitle}</span>
                  </span>
                </button>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ── Componente principal ──────────────────────────────────────────────────
function GCalendar({ events, categories, onCreate, onEventClick, createLabel = 'Criar', accent = '#FF6B35', dayStart = 7, dayEnd = 20, asideExtra = null }) {
  const isMobile = useGcIsMobile();
  const now = useGcNow();
  const todayIso = gcIso(now);
  const [view, setView] = React.useState(() => (window.matchMedia(GC_MOBILE_QUERY).matches ? 'day' : 'week'));
  const [cursor, setCursor] = React.useState(() => new Date(now.getFullYear(), now.getMonth(), now.getDate()));
  const [hidden, setHidden] = React.useState(() => new Set());

  // no celular a semana vira "3 dias" (7 colunas nao cabem com leitura)
  const activeView = isMobile && view === 'week' ? '3days' : !isMobile && view === '3days' ? 'week' : view;
  const views = isMobile
    ? [['day', 'Dia'], ['3days', '3 dias'], ['month', 'Mês'], ['list', 'Lista']]
    : [['day', 'Dia'], ['week', 'Semana'], ['month', 'Mês'], ['list', 'Lista']];

  const visible = React.useMemo(() => events.filter((e) => !hidden.has(e.category)), [events, hidden]);
  const byDate = React.useMemo(() => {
    const map = {};
    visible.forEach((e) => { (map[e.date] = map[e.date] || []).push(e); });
    Object.values(map).forEach((list) => list.sort(gcSortEvents));
    return map;
  }, [visible]);
  const markedDates = React.useMemo(() => new Set(Object.keys(byDate)), [byDate]);
  const countBy = React.useMemo(() => events.reduce((acc, e) => ({ ...acc, [e.category]: (acc[e.category] || 0) + 1 }), {}), [events]);

  const days = activeView === 'day' ? [cursor]
    : activeView === '3days' ? [0, 1, 2].map((i) => gcAddDays(cursor, i))
    : Array.from({ length: 7 }, (_, i) => gcAddDays(gcStartOfWeek(cursor), i));

  const step = (dir) => {
    if (activeView === 'month') setCursor(new Date(cursor.getFullYear(), cursor.getMonth() + dir, 1));
    else setCursor(gcAddDays(cursor, dir * { day: 1, '3days': 3, week: 7, list: 30 }[activeView]));
  };
  const goToday = () => setCursor(new Date(now.getFullYear(), now.getMonth(), now.getDate()));
  const openDay = (d) => { setCursor(d); setView('day'); };
  const toggleCat = (id) => setHidden((prev) => {
    const next = new Set(prev);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });

  const title = (() => {
    const m = cursor.getMonth();
    const y = cursor.getFullYear();
    if (activeView === 'month') return `${gcCap(GC_MONTHS[m])} de ${y}`;
    if (activeView === 'day') return isMobile ? `${GC_WEEK[cursor.getDay()]}, ${cursor.getDate()} de ${GC_MONTHS_SHORT[m]}` : `${GC_WEEK_LONG[cursor.getDay()]}, ${cursor.getDate()} de ${GC_MONTHS[m]} de ${y}`;
    if (activeView === 'list') return `A partir de ${cursor.getDate()} de ${GC_MONTHS_SHORT[m]}`;
    const a = days[0];
    const b = days[days.length - 1];
    if (a.getMonth() !== b.getMonth()) return `${a.getDate()} ${GC_MONTHS_SHORT[a.getMonth()]} – ${b.getDate()} ${GC_MONTHS_SHORT[b.getMonth()]} ${b.getFullYear()}`;
    return isMobile ? `${a.getDate()} – ${b.getDate()} de ${GC_MONTHS_SHORT[m]}` : `${a.getDate()} – ${b.getDate()} de ${GC_MONTHS[a.getMonth()]} de ${b.getFullYear()}`;
  })();

  const create = (slot) => onCreate(slot || { date: gcIso(cursor), time: null });
  const shared = { byDate, todayIso, accent, isMobile, onOpenDay: openDay, onCreate: create, onEvent: onEventClick };

  return (
    <div className="flex gap-5 items-start">
      <style>{'.gc-scroll::-webkit-scrollbar{width:8px}.gc-scroll::-webkit-scrollbar-thumb{background:#E2E8F0;border-radius:8px}.gc-noscroll{scrollbar-width:none}.gc-noscroll::-webkit-scrollbar{display:none}'}</style>

      {/* lateral (desktop) */}
      <aside className="hidden lg:flex flex-col gap-4 w-64 flex-shrink-0">
        <button onClick={() => create()}
          className="flex items-center gap-3 self-start pl-3 pr-6 py-3 rounded-2xl bg-white border border-slate-100 shadow-md hover:shadow-lg transition-shadow text-sm font-semibold text-slate-700">
          <span className="w-9 h-9 rounded-xl flex items-center justify-center text-white" style={{ background: accent }}><GcIcon d={GC_I.plus} size={18} sw={2.6} /></span>
          {createLabel}
        </button>
        <GcMiniMonth selected={cursor} onPick={(d) => setCursor(d)} markedDates={markedDates} accent={accent} todayIso={todayIso} />
        <div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-3">
          <p className="text-[11px] font-bold uppercase tracking-wide text-slate-400 px-1 mb-1.5">Minhas agendas</p>
          {categories.map((c) => {
            const on = !hidden.has(c.id);
            return (
              <button key={c.id} onClick={() => toggleCat(c.id)} className="w-full flex items-center gap-2.5 px-1.5 py-1.5 rounded-lg hover:bg-slate-50 text-left">
                <span className="w-[18px] h-[18px] rounded-[5px] flex items-center justify-center flex-shrink-0 border-2 transition-colors"
                  style={{ borderColor: c.color, background: on ? c.color : 'transparent' }}>
                  {on && <GcIcon d={GC_I.check} size={12} color="#fff" sw={3} />}
                </span>
                <span className={`text-sm flex-1 truncate ${on ? 'text-slate-700' : 'text-slate-400'}`}>{c.label}</span>
                <span className="text-xs text-slate-400">{countBy[c.id] || 0}</span>
              </button>
            );
          })}
        </div>
        {asideExtra}
      </aside>

      {/* calendario */}
      <section className="flex-1 min-w-0 bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
        <div className="flex flex-wrap items-center gap-2 px-3 sm:px-5 py-3 border-b border-slate-100">
          <button onClick={goToday} className="px-3.5 h-9 rounded-full border border-slate-200 text-sm font-semibold text-slate-600 hover:bg-slate-50">Hoje</button>
          <div className="flex">
            <button onClick={() => step(-1)} aria-label="Anterior" className="w-9 h-9 rounded-full flex items-center justify-center text-slate-500 hover:bg-slate-100"><GcIcon d={GC_I.left} /></button>
            <button onClick={() => step(1)} aria-label="Próximo" className="w-9 h-9 rounded-full flex items-center justify-center text-slate-500 hover:bg-slate-100"><GcIcon d={GC_I.right} /></button>
          </div>
          <h2 className="flex-1 min-w-0 text-base sm:text-lg font-semibold text-slate-800 truncate">{title}</h2>
          <div className="flex p-1 rounded-xl bg-slate-100 w-full sm:w-auto">
            {views.map(([id, label]) => (
              <button key={id} onClick={() => setView(id)}
                className={`flex-1 sm:flex-none px-3 sm:px-3.5 py-1.5 rounded-lg text-[13px] font-semibold transition-all ${activeView === id ? 'bg-white shadow-sm text-slate-800' : 'text-slate-500 hover:text-slate-700'}`}>
                {label}
              </button>
            ))}
          </div>
        </div>

        {/* filtros no celular/tablet */}
        <div className="lg:hidden flex gap-2 overflow-x-auto gc-noscroll px-3 sm:px-5 py-2.5 border-b border-slate-100">
          {categories.map((c) => {
            const on = !hidden.has(c.id);
            return (
              <button key={c.id} onClick={() => toggleCat(c.id)}
                className="flex items-center gap-1.5 flex-shrink-0 px-3 py-1.5 rounded-full border text-xs font-semibold transition-colors"
                style={on ? { borderColor: c.color, background: c.color + '14', color: c.color } : { borderColor: '#E2E8F0', color: '#94A3B8' }}>
                <span className="w-2 h-2 rounded-full" style={{ background: on ? c.color : '#CBD5E1' }} />
                {c.label}
              </button>
            );
          })}
        </div>

        {activeView === 'month' && <GcMonthView cursor={cursor} {...shared} />}
        {activeView === 'list' && <GcListView cursor={cursor} {...shared} />}
        {['day', '3days', 'week'].includes(activeView) && (
          <GcTimeGrid days={days} now={now} dayStart={dayStart} dayEnd={dayEnd} {...shared} />
        )}
      </section>

      {/* botao flutuante de criar (celular/tablet) */}
      <button onClick={() => create()} aria-label={createLabel}
        className="lg:hidden fixed z-40 w-14 h-14 rounded-2xl text-white shadow-lg flex items-center justify-center active:scale-95 transition-transform"
        style={{ background: accent, right: 'calc(16px + env(safe-area-inset-right, 0px))', bottom: 'calc(20px + env(safe-area-inset-bottom, 0px))' }}>
        <GcIcon d={GC_I.plus} size={26} sw={2.4} />
      </button>
    </div>
  );
}

Object.assign(window, { GCalendar, gcIso });
