import React, { useState, useRef, useEffect, useMemo } from "react";
import { createRoot } from "react-dom/client";
import { createClient } from "@supabase/supabase-js";

// Polyfill de crypto.randomUUID — em contextos não-seguros (ex: testar pelo
// IP da rede local via http, ou WebViews antigas) ele pode não existir.
// getRandomValues está sempre disponível, então geramos um UUID v4 na mão.
if (typeof crypto !== "undefined" && !crypto.randomUUID) {
  crypto.randomUUID = function () {
    const b = crypto.getRandomValues(new Uint8Array(16));
    b[6] = (b[6] & 0x0f) | 0x40;
    b[8] = (b[8] & 0x3f) | 0x80;
    const h = [...b].map((x) => x.toString(16).padStart(2, "0"));
    return `${h[0]}${h[1]}${h[2]}${h[3]}-${h[4]}${h[5]}-${h[6]}${h[7]}-${h[8]}${h[9]}-${h[10]}${h[11]}${h[12]}${h[13]}${h[14]}${h[15]}`;
  };
}

// Cliente Supabase — conexão com o backend real. As chaves vêm do
// index.html (window.JOTA_CONFIG). A chave publishable/anon é pública
// por design: o que protege os dados é o RLS no banco, não o segredo.
const supabase = createClient(
  window.JOTA_CONFIG.SUPABASE_URL,
  window.JOTA_CONFIG.SUPABASE_ANON_KEY
);

// Recorrência: a UI usa { type, ...resto }; o banco separa em
// recurrence_type (texto) + recurrence_data (jsonb). Estes dois
// helpers convertem de um formato pro outro nos dois sentidos.
function splitRecurrence(rec) {
  const { type, ...rest } = rec || { type: "diario" };
  return { recurrence_type: type || "diario", recurrence_data: rest };
}
function joinRecurrence(type, data) {
  return { type: type || "diario", ...(data || {}) };
}

// Horário do passo (opcional): guardado em recurrence.time como "HH:MM".
function timeToMinutes(hhmm) {
  if (!hhmm) return null;
  const [h, m] = hhmm.split(":").map(Number);
  return h * 60 + (m || 0);
}

// Converte um timestamp do banco em rótulo relativo ("Hoje, 14:32", "Ontem"...)
// no formato que as listas de pontos e notificações já exibem.
function formatRelativeDate(iso) {
  const d = new Date(iso);
  const now = new Date();
  const startOf = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
  const days = Math.round((startOf(now) - startOf(d)) / 86400000);
  if (days <= 0) return `Hoje, ${d.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })}`;
  if (days === 1) return "Ontem";
  if (days < 7) return `${days} dias atrás`;
  return d.toLocaleDateString("pt-BR", { day: "2-digit", month: "short" });
}

// "YYYY-MM" a partir de um ISO — usado pra agrupar/filtrar o histórico de Atons por mês.
function monthKey(iso) {
  if (!iso) return "";
  return iso.slice(0, 7);
}
function monthLabel(key) {
  const [y, m] = key.split("-").map(Number);
  if (!y || !m) return key;
  const label = new Date(y, m - 1, 1).toLocaleDateString("pt-BR", { month: "long", year: "numeric" });
  return label.charAt(0).toUpperCase() + label.slice(1);
}
import { Send, X, Sun, Moon, Search, BookOpen, User, Settings, Pencil, Plus, Check, Trash2, Repeat, GripVertical, CalendarDays, ChevronLeft, ChevronRight, ChevronUp, Star, SlidersHorizontal, ExternalLink, Heart, Circle, Flag, ChevronDown, Bell, Mic, Volume2, Sprout, Leaf, Flame, Gem, Info, Sparkle, Camera, AlertCircle, Clock, Eye, Share2, Copy, MessageCircle, History, SquarePen, Gift, Bookmark, FlaskConical, RotateCcw, UserPlus, Fingerprint, Ban, EyeOff, TrendingUp, Lock } from "lucide-react";

const LOGO_SRC = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTMwMSIgem9vbUFuZFBhbj0ibWFnbmlmeSIgdmlld0JveD0iMCAwIDk3NS43NSA0MDkuNDk5OTgzIiBoZWlnaHQ9IjU0NiIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCIgdmVyc2lvbj0iMS4wIj48ZGVmcz48Zy8+PGNsaXBQYXRoIGlkPSI4YWQzODNjZTU0Ij48cmVjdCB4PSIwIiB3aWR0aD0iODI5IiB5PSIwIiBoZWlnaHQ9IjI2NCIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJlNzQ2YjllMzAwIj48cGF0aCBkPSJNIDIxMyAxODUgTCAzNzYgMTg1IEwgMzc2IDIwMyBMIDIxMyAyMDMgWiBNIDIxMyAxODUgIiBjbGlwLXJ1bGU9Im5vbnplcm8iLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZjFjMTUzMzM5NyI+PHBhdGggZD0iTSAyMTMuOTkyMTg4IDIwMi45MDIzNDQgTCAyMTMuOTk2MDk0IDE4NS40MjE4NzUgTCAzNzUuOTE3OTY5IDE4NS40NzI2NTYgTCAzNzUuOTE0MDYyIDIwMi45NTMxMjUgWiBNIDIxMy45OTIxODggMjAyLjkwMjM0NCAiIGNsaXAtcnVsZT0ibm9uemVybyIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSIyOTBhODRlZjgwIj48cGF0aCBkPSJNIDAuODA4NTk0IDAuMjkyOTY5IEwgMTYzIDAuMjkyOTY5IEwgMTYzIDE4IEwgMC44MDg1OTQgMTggWiBNIDAuODA4NTk0IDAuMjkyOTY5ICIgY2xpcC1ydWxlPSJub256ZXJvIi8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9IjBkYTE4YTcyMDkiPjxwYXRoIGQ9Ik0gMC45OTIxODggMTcuOTAyMzQ0IEwgMC45OTYwOTQgMC40MjE4NzUgTCAxNjIuOTE3OTY5IDAuNDcyNjU2IEwgMTYyLjkxNDA2MiAxNy45NTMxMjUgWiBNIDAuOTkyMTg4IDE3LjkwMjM0NCAiIGNsaXAtcnVsZT0ibm9uemVybyIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSIzMjYzN2UzMzBmIj48cmVjdCB4PSIwIiB3aWR0aD0iMTYzIiB5PSIwIiBoZWlnaHQ9IjE4Ii8+PC9jbGlwUGF0aD48L2RlZnM+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgNzQsIDcyKSI+PGcgY2xpcC1wYXRoPSJ1cmwoIzhhZDM4M2NlNTQpIj48ZyBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjEiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuNTg2NDM2LCAxOTguNTk3NTc2KSI+PGc+PHBhdGggZD0iTSA5MC4xODc1IC0xNjEuMDQ2ODc1IEwgOTAuMTg3NSAtMzguOTIxODc1IEMgOTAuMTg3NSAtMjYuMzkwNjI1IDg2LjQyNTc4MSAtMTYuNDEwMTU2IDc4LjkwNjI1IC04Ljk4NDM3NSBDIDcxLjM5NDUzMSAtMS41NjY0MDYgNjEuNTU0Njg4IDIuMTQwNjI1IDQ5LjM5MDYyNSAyLjE0MDYyNSBDIDM4LjEwOTM3NSAyLjE0MDYyNSAyOC44NDM3NSAtMS4wMzEyNSAyMS41OTM3NSAtNy4zNzUgQyAxNC4zNTE1NjIgLTEzLjcyNjU2MiAxMC4xMDkzNzUgLTIyLjM2MzI4MSA4Ljg1OTM3NSAtMzMuMjgxMjUgTCAyNi4yOTY4NzUgLTM3LjAzMTI1IEMgMjcuMDE1NjI1IC0zMC40MTQwNjIgMjkuNTE5NTMxIC0yNS4wOTM3NSAzMy44MTI1IC0yMS4wNjI1IEMgMzguMTEzMjgxIC0xNy4wMzkwNjIgNDMuMzA0Njg4IC0xNS4wMzEyNSA0OS4zOTA2MjUgLTE1LjAzMTI1IEMgNTYuMTc5Njg4IC0xNS4wMzEyNSA2MS44MTI1IC0xNy4zNTE1NjIgNjYuMjgxMjUgLTIyIEMgNzAuNzU3ODEyIC0yNi42NTYyNSA3MyAtMzIuOTIxODc1IDczIC00MC43OTY4NzUgTCA3MyAtMTQzLjg1OTM3NSBMIDE2LjM3NSAtMTQzLjg1OTM3NSBMIDE2LjM3NSAtMTYxLjA0Njg3NSBaIE0gOTAuMTg3NSAtMTYxLjA0Njg3NSAiLz48L2c+PC9nPjwvZz48ZyBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjEiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDg1LjQxMzc4OSwgMTk4LjU5NzU3NikiPjxnLz48L2c+PC9nPjxnIGZpbGw9IiMwMDAwMDAiIGZpbGwtb3BhY2l0eT0iMSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI4LjYyNzA4LCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNzEuODQwMzcxLCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTUuMDUzNjYyLCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNTguMjY2OTUzLCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMDEuNDgwMjQ0LCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNDQuNzA0Nzg4LCAxOTguNTk3NTc2KSI+PGc+PHBhdGggZD0iTSA5NS44MTI1IDIuMTQwNjI1IEMgODQuMzYzMjgxIDIuMTQwNjI1IDczLjY3MTg3NSAtMC4wMDM5MDYyNSA2My43MzQzNzUgLTQuMjk2ODc1IEMgNTMuODA0Njg4IC04LjU4NTkzOCA0NS4wMzkwNjIgLTE0LjUzNTE1NiAzNy40Mzc1IC0yMi4xNDA2MjUgQyAyOS44MzIwMzEgLTI5Ljc0MjE4OCAyMy44ODI4MTIgLTM4LjUwNzgxMiAxOS41OTM3NSAtNDguNDM3NSBDIDE1LjMwMDc4MSAtNTguMzc1IDEzLjE1NjI1IC02OS4wNjY0MDYgMTMuMTU2MjUgLTgwLjUxNTYyNSBDIDEzLjE1NjI1IC05MS45NzI2NTYgMTUuMzAwNzgxIC0xMDIuNjY0MDYyIDE5LjU5Mzc1IC0xMTIuNTkzNzUgQyAyMy44ODI4MTIgLTEyMi41MTk1MzEgMjkuODMyMDMxIC0xMzEuMjg1MTU2IDM3LjQzNzUgLTEzOC44OTA2MjUgQyA0NS4wMzkwNjIgLTE0Ni40OTIxODggNTMuODA0Njg4IC0xNTIuNDQxNDA2IDYzLjczNDM3NSAtMTU2LjczNDM3NSBDIDczLjY3MTg3NSAtMTYxLjAzNTE1NiA4NC4zNjMyODEgLTE2My4xODc1IDk1LjgxMjUgLTE2My4xODc1IEMgMTA3LjI2OTUzMSAtMTYzLjE4NzUgMTE4LjAwNzgxMiAtMTYxLjAzNTE1NiAxMjguMDMxMjUgLTE1Ni43MzQzNzUgQyAxMzguMDUwNzgxIC0xNTIuNDQxNDA2IDE0Ni44MTY0MDYgLTE0Ni40OTIxODggMTU0LjMyODEyNSAtMTM4Ljg5MDYyNSBDIDE2MS44NDc2NTYgLTEzMS4yODUxNTYgMTY3Ljc1MzkwNiAtMTIyLjUxOTUzMSAxNzIuMDQ2ODc1IC0xMTIuNTkzNzUgQyAxNzYuMzM1OTM4IC0xMDIuNjY0MDYyIDE3OC40ODQzNzUgLTkxLjk3MjY1NiAxNzguNDg0Mzc1IC04MC41MTU2MjUgQyAxNzguNDg0Mzc1IC02OS4wNjY0MDYgMTc2LjMzNTkzOCAtNTguMzc1IDE3Mi4wNDY4NzUgLTQ4LjQzNzUgQyAxNjcuNzUzOTA2IC0zOC41MDc4MTIgMTYxLjg0NzY1NiAtMjkuNzQyMTg4IDE1NC4zMjgxMjUgLTIyLjE0MDYyNSBDIDE0Ni44MTY0MDYgLTE0LjUzNTE1NiAxMzguMDUwNzgxIC04LjU4NTkzOCAxMjguMDMxMjUgLTQuMjk2ODc1IEMgMTE4LjAwNzgxMiAtMC4wMDM5MDYyNSAxMDcuMjY5NTMxIDIuMTQwNjI1IDk1LjgxMjUgMi4xNDA2MjUgWiBNIDk1LjgxMjUgLTE1LjAzMTI1IEMgMTA3Ljk3NjU2MiAtMTUuMDMxMjUgMTE5LjAyMzQzOCAtMTcuOTM3NSAxMjguOTUzMTI1IC0yMy43NSBDIDEzOC44OTA2MjUgLTI5LjU2MjUgMTQ2Ljc2NTYyNSAtMzcuNDI5Njg4IDE1Mi41NzgxMjUgLTQ3LjM1OTM3NSBDIDE1OC4zOTg0MzggLTU3LjI5Njg3NSAxNjEuMzEyNSAtNjguMzQ3NjU2IDE2MS4zMTI1IC04MC41MTU2MjUgQyAxNjEuMzEyNSAtOTIuNjc5Njg4IDE1OC4zOTg0MzggLTEwMy43MjY1NjIgMTUyLjU3ODEyNSAtMTEzLjY1NjI1IEMgMTQ2Ljc2NTYyNSAtMTIzLjU5Mzc1IDEzOC44OTA2MjUgLTEzMS40Njg3NSAxMjguOTUzMTI1IC0xMzcuMjgxMjUgQyAxMTkuMDIzNDM4IC0xNDMuMTAxNTYyIDEwNy45NzY1NjIgLTE0Ni4wMTU2MjUgOTUuODEyNSAtMTQ2LjAxNTYyNSBDIDgzLjgyMDMxMiAtMTQ2LjAxNTYyNSA3Mi44NjMyODEgLTE0My4xMDE1NjIgNjIuOTM3NSAtMTM3LjI4MTI1IEMgNTMuMDA3ODEyIC0xMzEuNDY4NzUgNDUuMDkzNzUgLTEyMy41OTM3NSAzOS4xODc1IC0xMTMuNjU2MjUgQyAzMy4yODEyNSAtMTAzLjcyNjU2MiAzMC4zMjgxMjUgLTkyLjY3OTY4OCAzMC4zMjgxMjUgLTgwLjUxNTYyNSBDIDMwLjMyODEyNSAtNjguMzQ3NjU2IDMzLjI4MTI1IC01Ny4yOTY4NzUgMzkuMTg3NSAtNDcuMzU5Mzc1IEMgNDUuMDkzNzUgLTM3LjQyOTY4OCA1My4wMDc4MTIgLTI5LjU2MjUgNjIuOTM3NSAtMjMuNzUgQyA3Mi44NjMyODEgLTE3LjkzNzUgODMuODIwMzEyIC0xNS4wMzEyNSA5NS44MTI1IC0xNS4wMzEyNSBaIE0gOTUuODEyNSAtMTUuMDMxMjUgIi8+PC9nPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1MTUuMTQyMzE4LCAxOTguNTk3NTc2KSI+PGcvPjwvZz48L2c+PGcgZmlsbD0iIzAwMDAwMCIgZmlsbC1vcGFjaXR5PSIxIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NTguMzU1NjA5LCAxOTguNTk3NTc2KSI+PGc+PHBhdGggZD0iTSAxMDguNzAzMTI1IC0xNjEuMDQ2ODc1IEwgMTA4LjcwMzEyNSAtMTQzLjg1OTM3NSBMIDY0LjE0MDYyNSAtMTQzLjg1OTM3NSBMIDY0LjE0MDYyNSAwIEwgNDYuOTY4NzUgMCBMIDQ2Ljk2ODc1IC0xNDMuODU5Mzc1IEwgMi40MjE4NzUgLTE0My44NTkzNzUgTCAyLjQyMTg3NSAtMTYxLjA0Njg3NSBaIE0gMTA4LjcwMzEyNSAtMTYxLjA0Njg3NSAiLz48L2c+PC9nPjwvZz48ZyBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjEiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY0OC4wMDMwNjMsIDE5OC41OTc1NzYpIj48Zy8+PC9nPjwvZz48ZyBmaWxsPSIjMDAwMDAwIiBmaWxsLW9wYWNpdHk9IjEiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY5MS4yMTYzNTQsIDE5OC41OTc1NzYpIj48Zz48cGF0aCBkPSJNIDExNi43NSAwIEwgMTAzLjYwOTM3NSAtMzcuNTc4MTI1IEwgMzEuOTM3NSAtMzcuNTc4MTI1IEwgMTguNzgxMjUgMCBMIDAgMCBMIDU4LjI1IC0xNjEuMDQ2ODc1IEwgNzcuMjk2ODc1IC0xNjEuMDQ2ODc1IEwgMTM1LjU0Njg3NSAwIFogTSAzNy41NzgxMjUgLTUzLjk1MzEyNSBMIDk3Ljk2ODc1IC01My45NTMxMjUgTCA2Ny45MDYyNSAtMTQwLjY0MDYyNSBaIE0gMzcuNTc4MTI1IC01My45NTMxMjUgIi8+PC9nPjwvZz48L2c+PC9nPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZTc0NmI5ZTMwMCkiPjxnIGNsaXAtcGF0aD0idXJsKCNmMWMxNTMzMzk3KSI+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgMjEzLCAxODUpIj48ZyBjbGlwLXBhdGg9InVybCgjMzI2MzdlMzMwZikiPjxnIGNsaXAtcGF0aD0idXJsKCMyOTBhODRlZjgwKSI+PGcgY2xpcC1wYXRoPSJ1cmwoIzBkYTE4YTcyMDkpIj48cGF0aCBmaWxsPSIjMDAwMDAwIiBkPSJNIDAuOTkyMTg4IDE3LjkwMjM0NCBMIDAuOTk2MDk0IDAuNDIxODc1IEwgMTYyLjk0OTIxOSAwLjQ3MjY1NiBMIDE2Mi45NDUzMTIgMTcuOTUzMTI1IFogTSAwLjk5MjE4OCAxNy45MDIzNDQgIiBmaWxsLW9wYWNpdHk9IjEiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvZz48L2c+PC9nPjwvZz48L2c+PC9nPjwvc3ZnPg==";


const DISCLAIMER_PARAGRAPHS = [
  "O J-OTA é um assistente de inteligência artificial criado para ajudar você a entender melhor sobre pele, skincare e ciência cosmética. Como toda IA, ele pode cometer erros. Por isso, verifique sempre as respostas e use as orientações com senso crítico.",
  "As respostas são geradas com base em informações técnicas, conhecimento científico disponível, boas práticas da área, dados compartilhados por você e, quando aplicável, experiências gerais da comunidade. O objetivo é orientar de forma clara, educativa e responsável.",
  "Ainda assim, o J-OTA não é uma pessoa real, não é médico, dermatologista ou profissional de saúde. Suas orientações são sugestões gerais e não substituem avaliação individualizada, diagnóstico, prescrição ou acompanhamento profissional.",
  "Cada pele é única e pode ter necessidades diferentes de acordo com idade, fase da vida, histórico de saúde, sensibilidade, rotina, uso de medicamentos e condições específicas.",
  "Por segurança, recomendamos sempre consultar um médico ou dermatologista para avaliar o seu caso individualmente e validar qualquer informação, produto, ativo ou tratamento antes de iniciar, alterar ou interromper o uso, especialmente em crianças, adolescentes, gestantes, lactantes, pessoas idosas, casos de acne intensa, alergias, irritações, doenças de pele, realização de procedimentos estéticos, entre outras situações que exijam avaliação profissional individualizada.",
];

const DISCLAIMER_SHORT_TEXT = "Por segurança, recomendamos sempre consultar um médico ou dermatologista para avaliar o seu caso individualmente e validar qualquer informação, produto, ativo ou tratamento.";

function DisclaimerModal({ open, onClose }) {
  if (!open) return null;
  return (
    <div className="jota-honesty-overlay" onClick={onClose}>
      <div className="jota-honesty-card disclaimer" onClick={(e) => e.stopPropagation()}>
        <span className="jota-honesty-title">IMPORTANTE</span>
        <div className="jota-honesty-scroll">
          {DISCLAIMER_PARAGRAPHS.map((para, i) => (
            <p key={i} className="jota-honesty-text">{para}</p>
          ))}
        </div>
        <button className="jota-review-submit" onClick={onClose}>Entendi</button>
      </div>
    </div>
  );
}

function DisclaimerBar({ onOpen }) {
  return (
    <button className="jota-disclaimer-bar" onClick={onOpen}>
      <Info size={14} strokeWidth={1.8} />
      <span>{DISCLAIMER_SHORT_TEXT} <strong>Saiba mais.</strong></span>
    </button>
  );
}


// Toda conversa começa vazia: a saudação vive numa tela de boas-vindas
// centralizada (orbe + frase), não como bolha. Assim que a pessoa manda a
// primeira mensagem, a tela vira o fio normal de conversa.
const initialMessages = [];

// Saudações que sempre se apresentam (o J-OTA "brinca" a frase, mas nunca
// deixa de dizer quem é). Sorteada a cada nova conversa.
const CHAT_GREETINGS = [
  "Oi! Eu sou o J-OTA 🤍 Como posso te ajudar hoje?",
  "Olá! Aqui é o J-OTA. Em que posso te ajudar?",
  "Ei! Sou o J-OTA, seu parceiro de skincare. Como posso ajudar?",
  "Oi, tudo bem? Eu sou o J-OTA — no que posso te ajudar hoje?",
  "Olá! J-OTA na área. O que você quer descobrir hoje?",
];
const pickGreeting = () => CHAT_GREETINGS[Math.floor(Math.random() * CHAT_GREETINGS.length)];

// Sugestões de partida mostradas na tela de boas-vindas do chat.
// Frases-exemplo (rotativas) que ensinam o que dá pra pedir ao J-OTA — tanto
// ações na rotina quanto conselhos. Mostradas em janela girando na tela inicial.
const CHAT_SUGGESTIONS = [
  "Como usar o protetor solar na rotina?",
  "Incluir gel de limpeza na minha rotina",
  "Montar uma rotina noturna pra mim",
  "O que fazer pra reduzir a acne?",
  "Adicionar um hidratante de manhã",
  "Minha pele está oleosa, o que uso?",
  "Criar uma rotina pra pele mista",
  "Quando devo aplicar o ácido salicílico?",
];

const WEEKDAYS = ["dom", "seg", "ter", "qua", "qui", "sex", "sáb"];
// Ordem de exibição/seleção começando na SEGUNDA (índices de getDay(): 1=seg ... 0=dom).
const WEEKDAY_ORDER = [1, 2, 3, 4, 5, 6, 0];
const weekdayRank = (d) => (d === 0 ? 7 : d); // domingo por último ao ordenar

const RECUR_OPTIONS = [
  { type: "diario", label: "Todos os dias" },
  { type: "dias_semana", label: "Dias da semana" },
  { type: "intervalo", label: "A cada X dias" },
  { type: "datas", label: "Datas específicas" },
];

function todayKey(d = new Date()) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

function stripTime(d) {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
}

// ——— Recorrência com overrides por data (#18) ———
// step.recurrence = base { type, ...params, time? } + uma chave `overrides` opcional:
//   overrides.segments: [{ from:"YYYY-MM-DD", type, ...params, time? }]  ("deste dia em diante")
//   overrides.days:     { "YYYY-MM-DD": { time:"HH:MM"|null } }          ("somente este dia" — só horário)
// Passo sem `overrides` se comporta idêntico ao legado (retrocompatível).
// Datas "YYYY-MM-DD" comparam lexicograficamente = cronologicamente.

function baseRecurrence(rec) {
  const { overrides, from, ...pure } = rec || { type: "diario" };
  if (!pure.type) pure.type = "diario";
  return pure;
}

// Programação vigente numa data: o segmento de maior `from` <= key; senão a base.
function applicableSegment(rec, key) {
  const base = baseRecurrence(rec);
  const segs = (rec && rec.overrides && rec.overrides.segments) || [];
  let chosen = base;
  let chosenFrom = "";
  for (const seg of segs) {
    if (seg.from <= key && seg.from >= chosenFrom) { chosen = seg; chosenFrom = seg.from; }
  }
  const { from, ...pure } = chosen;
  return pure;
}

// Recorrência efetiva numa data (exceção de dia -> segmento -> base).
function effectiveRecurrence(step, date) {
  const rec = (step && step.recurrence) || { type: "diario" };
  const key = todayKey(date);
  const seg = applicableSegment(rec, key);
  const days = rec.overrides && rec.overrides.days;
  if (days && Object.prototype.hasOwnProperty.call(days, key)) {
    const dt = days[key] && days[key].time;
    const out = { ...seg };
    if (dt) out.time = dt; else delete out.time;
    return out;
  }
  return seg;
}

function effectiveTime(step, date) {
  return effectiveRecurrence(step, date).time || null;
}

// Regra bruta de um type (sem overrides) numa data.
function matchesRule(rec, date) {
  if (rec.type === "encerrado") return false; // segmento de remoção "deste dia em diante" (#18)
  if (rec.type === "dias_semana") {
    return (rec.diasSemana || []).includes(date.getDay());
  }
  if (rec.type === "intervalo") {
    const start = rec.dataInicio ? new Date(rec.dataInicio + "T00:00:00") : date;
    const diffDays = Math.round((stripTime(date) - stripTime(start)) / 86400000);
    if (diffDays < 0) return false;
    return diffDays % Math.max(1, rec.intervaloDias || 1) === 0;
  }
  if (rec.type === "datas") {
    return (rec.datas || []).includes(todayKey(date));
  }
  return true;
}

function isScheduledOn(step, date) {
  const rec = (step && step.recurrence) || { type: "diario" };
  const key = todayKey(date);
  const days = rec.overrides && rec.overrides.days;
  if (days && Object.prototype.hasOwnProperty.call(days, key)) {
    if (days[key] && days[key].skip) return false; // remoção "somente hoje" (#18)
    return true; // exceção de dia (horário) = acontece
  }
  return matchesRule(applicableSegment(rec, key), date);
}

// —— Helpers de edição com escopo (#18) ——
// Limpa um objeto de recorrência (tira overrides/from e horário vazio).
function cleanRec(rec) {
  const { overrides, from, ...rest } = rec || { type: "diario" };
  if (!rest.type) rest.type = "diario";
  if (rest.time === "" || rest.time == null) delete rest.time;
  return rest;
}

// Assinatura só da programação (ignora horário) — pra detectar mudança de frequência.
function recSchedSig(rec) {
  const b = cleanRec(rec);
  if (b.type === "dias_semana") return "w:" + [...(b.diasSemana || [])].sort((x, y) => x - y).join(",");
  if (b.type === "intervalo") return "i:" + (b.intervaloDias || 1) + "@" + (b.dataInicio || "");
  if (b.type === "datas") return "d:" + [...(b.datas || [])].sort().join(",");
  return "diario";
}
function recTimeOf(rec) { return cleanRec(rec).time || ""; }

function recChanged(a, b) { return recSchedSig(a) !== recSchedSig(b) || recTimeOf(a) !== recTimeOf(b); }
function isTimeOnlyChange(a, b) { return recSchedSig(a) === recSchedSig(b) && recTimeOf(a) !== recTimeOf(b); }

// Um passo é "recorrente" (vale perguntar escopo) se repete em mais de um dia.
function isRecurringStep(rec) {
  const b = baseRecurrence(rec);
  if (b.type === "datas") return (b.datas || []).length > 1;
  return true;
}

function scheduleText(rec) {
  const b = cleanRec(rec);
  if (b.type === "diario") return "todos os dias";
  return recurrenceLabel(b) || "—";
}

// Frase curta pro pop-up ("de todo dia para SEG | QUA | SEX · horário de 08:00 para 20:00").
function describeRecurrenceChange(fromRec, toRec) {
  const parts = [];
  if (recSchedSig(fromRec) !== recSchedSig(toRec)) parts.push(`de ${scheduleText(fromRec)} para ${scheduleText(toRec)}`);
  const ft = recTimeOf(fromRec), tt = recTimeOf(toRec);
  if (ft !== tt) {
    if (!ft && tt) parts.push(`horário definido para ${tt}`);
    else if (ft && !tt) parts.push("horário removido");
    else parts.push(`horário de ${ft} para ${tt}`);
  }
  return parts.join(" · ");
}

// Constrói o novo objeto recurrence (base + overrides) conforme o escopo escolhido.
// anchorKey = "YYYY-MM-DD" do dia de referência (hoje, na v1).
function applyRecurrenceScoped(step, draftRec, scope, anchorKey) {
  const current = (step && step.recurrence) || { type: "diario" };
  const base = baseRecurrence(current);
  const ov = current.overrides || {};
  const draft = cleanRec(draftRec);

  if (scope === "all") {
    return { ...draft }; // sem overrides — limpa exceções/segmentos (decisão 2)
  }
  if (scope === "forward") {
    const segsBefore = (ov.segments || []).filter((s) => s.from < anchorKey);
    const segments = [...segsBefore, { from: anchorKey, ...draft }]
      .sort((a, b) => (a.from < b.from ? -1 : a.from > b.from ? 1 : 0));
    const days = {};
    Object.keys(ov.days || {}).forEach((k) => { if (k < anchorKey) days[k] = ov.days[k]; });
    const overrides = { segments };
    if (Object.keys(days).length) overrides.days = days;
    return { ...base, overrides };
  }
  // scope === "day" — só horário do dia âncora
  const days = { ...(ov.days || {}) };
  days[anchorKey] = { time: draft.time || null };
  const overrides = { ...ov, days };
  return { ...base, overrides };
}

// Remoção de um passo com escopo (mesmo modelo de overrides do #18, sem apagar
// a linha do passo — só "all" apaga de vez, tratado fora daqui via onRemoveStep):
//  "day"     -> exceção de hoje marcada como "skip" (some só hoje, resto intacto)
//  "forward" -> segmento "encerrado" a partir de hoje (some daqui pra frente, histórico anterior intacto)
function applyRemovalScoped(step, scope, anchorKey) {
  const current = (step && step.recurrence) || { type: "diario" };
  const base = baseRecurrence(current);
  const ov = current.overrides || {};
  if (scope === "day") {
    const days = { ...(ov.days || {}) };
    days[anchorKey] = { skip: true };
    const overrides = { ...ov, days };
    return { ...base, overrides };
  }
  if (scope === "forward") {
    const segsBefore = (ov.segments || []).filter((s) => s.from < anchorKey);
    const segments = [...segsBefore, { from: anchorKey, type: "encerrado" }];
    const days = {};
    Object.keys(ov.days || {}).forEach((k) => { if (k < anchorKey) days[k] = ov.days[k]; });
    const overrides = { segments };
    if (Object.keys(days).length) overrides.days = days;
    return { ...base, overrides };
  }
  return base;
}

function recurrenceLabel(rec) {
  if (!rec) return null;
  if (rec.type === "diario") return "Todos os dias";
  if (rec.type === "dias_semana") {
    const dias = rec.diasSemana || [];
    // Todos os 7 dias marcados = uso diário
    if (dias.length === 7) return "Todos os dias";
    return dias.length
      ? [...dias].sort((a, b) => weekdayRank(a) - weekdayRank(b)).map((i) => WEEKDAYS[i].toUpperCase()).join(" | ")
      : "DIAS DA SEMANA";
  }
  if (rec.type === "intervalo") return `a cada ${rec.intervaloDias || 1} dias`;
  if (rec.type === "datas") {
    const n = (rec.datas || []).length;
    return n === 0 ? "sem datas" : n === 1 ? formatDateShort(rec.datas[0]) : `${n} datas`;
  }
  return null;
}

function formatDateShort(iso) {
  const d = new Date(iso + "T00:00:00");
  return d.toLocaleDateString("pt-BR", { day: "2-digit", month: "short" });
}

function formatDateLong(d) {
  const s = d.toLocaleDateString("pt-BR", { weekday: "long", day: "2-digit", month: "long" });
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function capitalize(s) {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

// Extrai da resposta do J-OTA um texto principal + uma lista de "opções"
// clicáveis, pra pessoa poder tocar em vez de digitar. Reconhece dois
// formatos, sem exigir nenhuma chamada extra de IA:
//  1) linhas que começam com "→ " (formato que o prompt do sistema pede
//     quando o J-OTA está oferecendo uma escolha real).
//  2) fallback: uma lista numerada/com marcador (1. 2. / - / •) no final
//     da mensagem, com itens curtos — o jeito que o Claude já formata
//     escolhas por padrão, mesmo sem instrução especial.
function splitAssistantContent(content) {
  if (!content) return { text: "", options: [] };
  const lines = content.split("\n");

  const arrowLines = lines.filter((l) => l.trim().startsWith("→ "));
  if (arrowLines.length >= 2) {
    const options = arrowLines.map((l) => l.trim().slice(2).trim()).filter(Boolean);
    const text = lines.filter((l) => !l.trim().startsWith("→ "));
    while (text.length && text[text.length - 1].trim() === "") text.pop();
    return { text: text.join("\n"), options };
  }

  const listItemRe = /^\s*(?:[-•*]|\d+[.)])\s+(.+)$/;
  let start = lines.length;
  for (let i = lines.length - 1; i >= 0; i--) {
    const line = lines[i];
    if (line.trim() === "") continue;
    const m = line.match(listItemRe);
    if (!m || m[1].length > 70) break;
    start = i;
  }
  const trailing = lines.slice(start).filter((l) => l.trim() !== "");
  if (trailing.length >= 2 && trailing.every((l) => listItemRe.test(l))) {
    const options = trailing.map((l) => l.match(listItemRe)[1].trim());
    const text = lines.slice(0, start);
    while (text.length && text[text.length - 1].trim() === "") text.pop();
    return { text: text.join("\n"), options };
  }

  return { text: content, options: [] };
}

// Renderiza markdown simples (negrito, itálico, título "#", divisória "---")
// como elementos de verdade em vez de deixar os símbolos crus na tela —
// sem depender de nenhuma lib de markdown.
function renderInlineMarkdown(text) {
  const lines = text.split("\n");
  const nodes = [];
  lines.forEach((line, li) => {
    if (li > 0) nodes.push("\n");
    if (/^\s*(-{3,}|\*{3,})\s*$/.test(line)) return; // divisória "---"/"***": não faz sentido numa bolha de chat
    const heading = line.match(/^\s{0,3}#{1,6}\s+(.+)$/);
    const content = heading ? heading[1] : line;
    const parts = content.split(/\*\*(.+?)\*\*/g).flatMap((part, i) => {
      if (i % 2 === 1) return [<strong key={`${li}-b-${i}`}>{part}</strong>];
      return part.split(/\*(.+?)\*/g).map((seg, j) => (
        j % 2 === 1 ? <em key={`${li}-${i}-i-${j}`}>{seg}</em> : seg
      ));
    });
    if (heading) nodes.push(<strong key={`h-${li}`}>{parts}</strong>);
    else nodes.push(...parts);
  });
  return nodes;
}

function startOfWeek(date) {
  const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  const dow = (d.getDay() + 6) % 7; // 0=segunda ... 6=domingo (semana começa na segunda)
  d.setDate(d.getDate() - dow);
  return d;
}

function getWeekDates(refDate) {
  const start = startOfWeek(refDate);
  return Array.from({ length: 7 }, (_, i) => {
    const d = new Date(start);
    d.setDate(start.getDate() + i);
    return d;
  });
}

function getMonthGrid(refDate) {
  const firstOfMonth = new Date(refDate.getFullYear(), refDate.getMonth(), 1);
  const gridStart = startOfWeek(firstOfMonth);
  const weeks = [];
  const cursor = new Date(gridStart);
  for (let w = 0; w < 6; w++) {
    const week = Array.from({ length: 7 }, (_, i) => {
      const d = new Date(cursor);
      d.setDate(cursor.getDate() + i);
      return d;
    });
    weeks.push(week);
    cursor.setDate(cursor.getDate() + 7);
  }
  return weeks;
}

function stepsScheduledOnDate(routines, date) {
  const result = [];
  routines.forEach((r) => {
    r.steps.forEach((s) => {
      if (isScheduledOn(s, date)) {
        result.push({ routineId: r.id, routineName: r.name, stepId: s.id, label: s.label, photo: s.photo, completions: s.completions });
      }
    });
  });
  return result;
}

const SKIN_TYPES = ["Oleosa", "Mista", "Seca", "Normal", "Sensível", "Acneica", "Madura", "Não sei identificar"];

const SKIN_TYPE_HELP = {
  "Oleosa": "Brilho e oleosidade ao longo do dia, principalmente na testa, nariz e queixo.",
  "Mista": "Oleosa na zona T (testa, nariz, queixo), normal ou seca no resto do rosto.",
  "Seca": "Sensação de repuxamento, aspereza ou descamação frequente.",
  "Normal": "Sem grandes queixas de oleosidade ou ressecamento no dia a dia.",
  "Sensível": "Arde, coça ou fica vermelha com facilidade a produtos novos.",
  "Acneica": "Tendência a cravos, espinhas e poros obstruídos com frequência.",
  "Madura": "Foco maior em firmeza, elasticidade e linhas de expressão.",
  "Não sei identificar": "Sem problema — o J-OTA te ajuda a entender melhor com o tempo.",
};

const SKIN_CONCERNS = [
  "Manchas", "Melasma", "Oleosidade excessiva", "Poros dilatados", "Acne", "Cravos",
  "Ressecamento", "Desidratação", "Vermelhidão", "Rosácea", "Dermatite", "Sensibilidade",
  "Olheiras", "Bolsas nos olhos", "Rugas", "Linhas finas", "Textura irregular", "Flacidez", "Queda capilar",
  "Caspa", "Coceira no couro cabeludo", "Estrias", "Celulite", "Escurecimento de áreas do corpo", "Perda de viço",
];


const STEP_TYPES = ["Limpeza", "Esfoliação", "Hidratação", "Tratamento", "Máscara", "Contorno dos Olhos", "Proteção Solar", "Depilação", "Desodorante"];

const BODY_AREAS = ["Face", "Corpo", "Couro Cabeludo", "Cabelos", "Pelos", "Axilas", "Braços", "Pernas", "Virilha", "Mãos", "Pés", "Lábios"];

// Ativos (ingredientes ativos) — funcionam como TAG do produto: usados
// pra filtrar na Busca e, depois, alimentar a recomendação personalizada.
// Estrutura expansível: pra adicionar um ativo novo, basta incluir aqui
// (com `pro: true` se for de uso sob orientação profissional). Nada mais
// na lógica do filtro precisa mudar.
const PRO_ACTIVE_LABEL = "Uso sob orientação profissional";
const PRODUCT_ACTIVES = [
  { name: "Ácido alfa-lipoico" },
  { name: "Ácido azelaico" },
  { name: "Ácido capriloil salicílico" },
  { name: "Ácido cítrico" },
  { name: "Ácido ferúlico" },
  { name: "Ácido glicólico" },
  { name: "Ácido hialurônico" },
  { name: "Ácido kójico" },
  { name: "Ácido lactobiônico" },
  { name: "Ácido lático" },
  { name: "Ácido málico" },
  { name: "Ácido mandélico" },
  { name: "Ácido poliglutâmico" },
  { name: "Ácido salicílico" },
  { name: "Ácido succínico" },
  { name: "Ácido tartárico" },
  { name: "Ácido tranexâmico" },
  { name: "Adenosina" },
  { name: "Alantoína" },
  { name: "Alfa-arbutin" },
  { name: "Aloe vera" },
  { name: "Arbutin" },
  { name: "Argireline" },
  { name: "Astaxantina" },
  { name: "Aveia coloidal" },
  { name: "Bakuchiol" },
  { name: "Beta-glucana" },
  { name: "Bisabolol" },
  { name: "Cafeína" },
  { name: "Calêndula" },
  { name: "Camomila" },
  { name: "Cassia alata" },
  { name: "Centella asiática" },
  { name: "Ceramidas" },
  { name: "Chá verde" },
  { name: "Coenzima Q10" },
  { name: "Colágeno" },
  { name: "Cobre" },
  { name: "Dióxido de titânio" },
  { name: "Ectoína" },
  { name: "Enxofre" },
  { name: "Esqualano" },
  { name: "Extrato de alcaçuz" },
  { name: "Fatores de crescimento", pro: true },
  { name: "Filtros UV" },
  { name: "Glabridina" },
  { name: "Glicerina" },
  { name: "Glutationa" },
  { name: "Gluconolactona" },
  { name: "Hexilresorcinol" },
  { name: "Hidroquinona", pro: true },
  { name: "Idebenona" },
  { name: "Madecassoside" },
  { name: "Manteiga de karité" },
  { name: "Matrixyl" },
  { name: "Niacinamida" },
  { name: "Óxido de zinco" },
  { name: "Pantenol" },
  { name: "Papaína" },
  { name: "Peptídeos" },
  { name: "Peróxido de benzoíla", pro: true },
  { name: "Polifenóis" },
  { name: "Resorcinol" },
  { name: "Resveratrol" },
  { name: "Retinal" },
  { name: "Retinil palmitato" },
  { name: "Retinol" },
  { name: "Silício orgânico" },
  { name: "Tensoativos" },
  { name: "Thiamidol" },
  { name: "Tretinoína", pro: true },
  { name: "Ureia" },
  { name: "Vitamina C" },
  { name: "Vitamina E" },
  { name: "Vitamina K" },
  { name: "Zinco" },
  { name: "Zinco PCA" },
];
// Nomes dos ativos que exigem orientação profissional (lookup rápido).
const RESTRICTED_ACTIVES = new Set(PRODUCT_ACTIVES.filter((a) => a.pro).map((a) => a.name));
// Um produto "tem" um ativo se estiver tagueado (p.actives) ou, enquanto os
// produtos não estiverem tagueados, se o ingrediente-chave bater pelo nome.
function productHasActive(p, ativoName) {
  if ((p.actives || []).includes(ativoName)) return true;
  return (p.keyIngredients || []).some((ki) => ki.toLowerCase().includes(ativoName.toLowerCase()));
}

// Populado em runtime por loadCatalog() a partir da tabela `products` do Supabase.
const PRODUCT_CATALOG = [];

const PRICE_MIN = 0;
const PRICE_MAX = 500;

const SIZE_BRACKETS = [
  { id: "s1", label: "Mini (até 15)", test: (p) => p.size <= 15 },
  { id: "s2", label: "Pequeno (16–30)", test: (p) => p.size > 15 && p.size <= 30 },
  { id: "s3", label: "Médio (31–75)", test: (p) => p.size > 30 && p.size <= 75 },
  { id: "s4", label: "Grande (76–150)", test: (p) => p.size > 75 && p.size <= 150 },
  { id: "s5", label: "Extra grande (151–300)", test: (p) => p.size > 150 && p.size <= 300 },
  { id: "s6", label: "Gigante (301+)", test: (p) => p.size > 300 },
];

const defaultRoutines = [
  {
    id: "r-manha",
    name: "Manhã",
    steps: [
      { id: "s1", label: "Limpador Facial Suave", productId: null, photo: "icon:sabonete", recurrence: { type: "diario" }, completions: {} },
      { id: "s2", label: "Sérum Vitamina C 10%", productId: null, photo: "icon:serum", recurrence: { type: "diario" }, completions: {} },
      { id: "s3", label: "Hidratante Barreira Cutânea", productId: null, photo: "icon:pote", recurrence: { type: "diario" }, completions: {} },
      { id: "s4", label: "Protetor Solar FPS 60", productId: null, photo: "icon:protetor", recurrence: { type: "diario" }, completions: {} },
    ],
  },
  {
    id: "r-noite",
    name: "Noite",
    steps: [
      { id: "s5", label: "Demaquilante", productId: null, photo: null, recurrence: { type: "diario" }, completions: {} },
      { id: "s6", label: "Limpador Facial Suave", productId: null, photo: "icon:sabonete", recurrence: { type: "diario" }, completions: {} },
      { id: "s7", label: "Retinal 0,05%", productId: null, photo: "icon:serum", recurrence: { type: "dias_semana", diasSemana: [1, 3, 5] }, completions: {} },
      { id: "s8", label: "Hidratante Barreira Cutânea", productId: null, photo: "icon:pote", recurrence: { type: "diario" }, completions: {} },
    ],
  },
];

// Miniaturas vetoriais pros produtos personalizados (criados pelo usuário).
// Guardadas no passo como photo = "icon:<id>" — persiste em photo_url no banco.
const CUSTOM_PRODUCT_ICONS = [
  { id: "serum", label: "Sérum", d: (
    <>
      <rect x="8.5" y="9" width="7" height="12" rx="2" />
      <path d="M10.5 9V7h3v2" />
      <circle cx="12" cy="3.6" r="1.4" />
      <path d="M12 5v2" />
    </>
  ) },
  { id: "bisnaga", label: "Bisnaga", d: (
    <>
      <path d="M8.5 8.5h7l1 11a1.5 1.5 0 0 1-1.5 1.5H9a1.5 1.5 0 0 1-1.5-1.5l1-11z" />
      <rect x="10" y="3.5" width="4" height="3.5" rx="1" />
    </>
  ) },
  { id: "pote", label: "Pote de creme", d: (
    <>
      <rect x="6.5" y="9.5" width="11" height="10.5" rx="2.5" />
      <rect x="7.5" y="5.5" width="9" height="4" rx="1.5" />
    </>
  ) },
  { id: "sabonete", label: "Sabonete", d: (
    <>
      <rect x="5" y="9" width="14" height="9" rx="4.5" />
      <circle cx="17.5" cy="6" r="1" />
      <circle cx="20" cy="3.8" r="1.4" />
    </>
  ) },
  { id: "pump", label: "Frasco pump", d: (
    <>
      <rect x="8" y="10" width="8" height="11" rx="2" />
      <path d="M11 10V6.5h2V10" />
      <path d="M11 6.5V4.5h5.5v2.5" />
    </>
  ) },
  { id: "protetor", label: "Protetor solar", d: (
    <>
      <rect x="7.5" y="8" width="9" height="13" rx="2" />
      <rect x="9.5" y="4" width="5" height="4" rx="1" />
      <circle cx="12" cy="14.5" r="2.4" />
    </>
  ) },
  { id: "spray", label: "Spray", d: (
    <>
      <rect x="9" y="10.5" width="7" height="10.5" rx="2" />
      <path d="M11 10.5V6h4.5l1.5 2" />
      <circle cx="19.5" cy="4.5" r="0.5" />
      <circle cx="21" cy="6.8" r="0.5" />
      <circle cx="19" cy="8.5" r="0.5" />
    </>
  ) },
  { id: "capsula", label: "Cápsula", d: (
    <>
      <rect x="4.5" y="8.8" width="15" height="6.5" rx="3.25" transform="rotate(-22 12 12)" />
      <path d="M10.8 8.4l2.4 7.2" />
    </>
  ) },
  { id: "mascara", label: "Máscara facial", d: (
    <>
      <path d="M7 4.5h10a3 3 0 0 1 3 3V13a8 8 0 0 1-16 0V7.5a3 3 0 0 1 3-3z" />
      <path d="M9 11h1.6M13.4 11H15" />
      <path d="M10.3 15.5c1 .9 2.4.9 3.4 0" />
    </>
  ) },
  { id: "generico", label: "Skincare", d: (
    <>
      <rect x="5.5" y="7.5" width="13" height="13" rx="2" />
      <path d="M5.5 11h13" />
      <path d="M12 2.4l.7 1.5 1.5.7-1.5.7-.7 1.5-.7-1.5-1.5-.7 1.5-.7z" />
    </>
  ) },
];

function CustomProductIcon({ type, size = 22 }) {
  const icon = CUSTOM_PRODUCT_ICONS.find((i) => i.id === type) || CUSTOM_PRODUCT_ICONS[CUSTOM_PRODUCT_ICONS.length - 1];
  return (
    <svg
      width={size}
      height={size}
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      {icon.d}
    </svg>
  );
}

function ProductThumb({ name, photo, size = 26 }) {
  const initial = (name || "?").trim().charAt(0).toUpperCase();
  const radius = Math.round(size * 0.22);
  // Produto personalizado: photo = "icon:<id>" → miniatura vetorial
  if (photo && photo.startsWith("icon:")) {
    return (
      <span
        className="jota-product-thumb jota-product-thumb-placeholder"
        style={{ width: size, height: size, borderRadius: radius }}
      >
        <CustomProductIcon type={photo.slice(5)} size={Math.round(size * 0.62)} />
      </span>
    );
  }
  if (photo) {
    return (
      <img
        src={photo}
        alt={name}
        className="jota-product-thumb"
        style={{ width: size, height: size, borderRadius: radius }}
      />
    );
  }
  return (
    <span
      className="jota-product-thumb jota-product-thumb-placeholder"
      style={{ width: size, height: size, fontSize: size * 0.38, borderRadius: radius }}
    >
      {initial}
    </span>
  );
}

function JotaOrb({ size = 34, speaking = false }) {
  // Orbes grandes (navbar, voz, onboarding) ganham as camadas ricas; os
  // pequenos (avatares do chat, que podem aparecer aos montes) ficam leves.
  const detailed = size >= 40;
  return (
    <span className={`jota-orb-wrap ${speaking ? "speaking" : ""}`} style={{ width: Math.round(size * 1.38), height: Math.round(size * 1.38) }}>
      <span className="jota-orb-shell" />
      {detailed && <span className="jota-orb-atom"><span className="jota-orb-dot3d" /></span>}
      <span className="jota-orb-core" style={{ width: size, height: size }}>
        {detailed && <span className="jota-orb-shimmer" />}
        {detailed && <span className="jota-orb-rim" />}
      </span>
    </span>
  );
}

// Estrelas orgânicas: aparecem/somem em pontos aleatórios dentro da "zona" do estágio.
function skyRnd(a, b) { return a + Math.random() * (b - a); }
const SKY_STAR_ZONE = {
  amanhecer: { l: [4, 40], t: [16, 80] },  // parte rosa (esquerda)
  fimtarde:  { l: [58, 95], t: [16, 80] },  // parte roxa (direita)
  _default:  { l: [4, 96], t: [10, 85] },   // madrugada / noite: céu todo
};
const SKY_STAR_COUNT = { madrugada: 6, noite: 14, amanhecer: 4, fimtarde: 4 };

// Nuvens por estágio: t/l em %, dur/delay em s (delay negativo = fase/posição inicial).
const SKY_CLOUDS = {
  amanhecer: [
    { t: 22, l: 12, dur: 270, delay: -66.6, cls: "sm" },
    { t: 60, l: 40, dur: 315, delay: -136.5, cls: "wispy" },
    { t: 32, l: 70, dur: 285, delay: -180.5, cls: "" },
    { t: 66, l: 92, dur: 330, delay: -257.4, cls: "sm wispy" },
  ],
  manha: [
    { t: 56, l: 8, dur: 275, delay: -60.5, cls: "sm" },
    { t: 30, l: 30, dur: 255, delay: -93.5, cls: "" },
    { t: 62, l: 45, dur: 310, delay: -144.7, cls: "sm wispy" },
    { t: 40, l: 66, dur: 265, delay: -160.8, cls: "sm" },
    { t: 26, l: 88, dur: 325, delay: -244.8, cls: "" },
  ],
  tarde: [
    { t: 26, l: 10, dur: 285, delay: -66.5, cls: "wispy" },
    { t: 62, l: 32, dur: 255, delay: -96.9, cls: "sm wispy" },
    { t: 34, l: 52, dur: 325, delay: -166.8, cls: "wispy" },
    { t: 70, l: 72, dur: 270, delay: -174.6, cls: "sm wispy" },
    { t: 44, l: 92, dur: 310, delay: -241.8, cls: "wispy" },
  ],
};

// Pássaros (gaivotas) por estágio.
const SKY_BIRDS = {
  amanhecer: [
    { t: 28, l: 46, w: 20, delay: -56.8 },
    { t: 35, l: 52, w: 12, delay: -61.6, b2: true },
    { t: 24, l: 98, w: 16, delay: -116.8 },
    { t: 52, l: -6, w: 14, delay: -15.2, b2: true },
  ],
  fimtarde: [
    // par da frente, espaçado (um em cima, outro embaixo)
    { t: 22, l: 42, w: 16, delay: -53.6 },
    { t: 58, l: 32, w: 14, delay: -45.6, b2: true },
    // bando de 4 vindo atrás
    { t: 44, l: 12, w: 20, delay: -29.6 },
    { t: 28, l: 4, w: 15, delay: -23.2, b2: true },
    { t: 54, l: 6, w: 13, delay: -24.8 },
    { t: 37, l: -4, w: 12, delay: -16.8, b2: true },
    // solitário
    { t: 34, l: 72, w: 15, delay: -77.6 },
  ],
};

// 6 períodos do dia (banner dinâmico da Home). Faixas de hora fechadas.
function skyStage(hour) {
  if (hour < 5) return "madrugada";   // 00:00–04:59
  if (hour < 9) return "amanhecer";   // 05:00–08:59
  if (hour < 12) return "manha";      // 09:00–11:59
  if (hour < 15) return "tarde";      // 12:00–14:59 (início da tarde)
  if (hour < 18) return "fimtarde";   // 15:00–17:59
  return "noite";                     // 18:00–23:59
}

// Estrela orgânica: some no vale do piscar e renasce em outro ponto da zona.
// Período e fase bem espalhados (evita "batimento" que sincronizaria o piscar).
function SkyStar({ zone }) {
  const [pos, setPos] = useState(() => ({ l: skyRnd(zone.l[0], zone.l[1]), t: skyRnd(zone.t[0], zone.t[1]) }));
  const [meta] = useState(() => {
    const dur = skyRnd(5, 13);
    return { size: Math.random() < 0.34 ? 2 : 1.5, dur, delay: -skyRnd(0, dur) };
  });
  return (
    <i
      className="jota-sky-star"
      style={{ left: pos.l + "%", top: pos.t + "%", width: meta.size, height: meta.size, animationDuration: meta.dur + "s", animationDelay: meta.delay + "s" }}
      onAnimationIteration={() => setPos({ l: skyRnd(zone.l[0], zone.l[1]), t: skyRnd(zone.t[0], zone.t[1]) })}
    />
  );
}

// Gaivota: silhueta em SVG batendo asa; loop "sail" leva o horizontal.
function SkyBird({ t, l, w, delay, b2 }) {
  return (
    <span
      className={`jota-sky-bird sail ${b2 ? "b2" : ""}`}
      style={{ top: t + "%", left: l + "%", width: w, height: w / 2, "--sail-dur": "120s", "--sail-delay": delay + "s" }}
    >
      <svg viewBox="0 0 16 8" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round">
        <path className="jota-sky-wing" d="M1 6 Q4 1 8 5 Q12 1 15 6" />
      </svg>
    </span>
  );
}

function SkyBanner() {
  const [now, setNow] = useState(new Date());

  // Atualiza exatamente na virada de cada minuto (em vez de um intervalo fixo,
  // que podia deixar o relógio até 30s atrasado em relação ao do celular).
  useEffect(() => {
    let timeoutId;
    const scheduleNextTick = () => {
      const msToNextMinute = 60000 - (Date.now() % 60000);
      timeoutId = setTimeout(() => {
        setNow(new Date());
        scheduleNextTick();
      }, msToNextMinute + 200);
    };
    scheduleNextTick();
    return () => clearTimeout(timeoutId);
  }, []);

  const stage = skyStage(now.getHours());
  const isMoon = stage === "madrugada" || stage === "noite";
  const clouds = SKY_CLOUDS[stage] || [];
  const birds = SKY_BIRDS[stage] || [];
  const starCount = SKY_STAR_COUNT[stage] || 0;
  const zone = SKY_STAR_ZONE[stage] || SKY_STAR_ZONE._default;

  const weekday = now.toLocaleDateString("pt-BR", { weekday: "long" });
  const weekdayCap = weekday.charAt(0).toUpperCase() + weekday.slice(1);
  const dateStr = now.toLocaleDateString("pt-BR", { day: "numeric", month: "long" });
  const timeStr = now.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" });

  return (
    <div className={`jota-sky-banner ${stage}`}>
      <div className="jota-sky-haze" />
      {Array.from({ length: starCount }).map((_, i) => (
        <SkyStar key={`st-${stage}-${i}`} zone={zone} />
      ))}
      {clouds.map((c, i) => (
        <span
          key={`cl-${i}`}
          className={`jota-sky-cloud sail ${c.cls}`}
          style={{ top: c.t + "%", left: c.l + "%", "--sail-dur": c.dur + "s", "--sail-delay": c.delay + "s" }}
        />
      ))}
      <span className={`jota-sky-orb ${isMoon ? "moon" : "sun"}`} />
      {birds.map((b, i) => (
        <SkyBird key={`bd-${i}`} {...b} />
      ))}
      <div className="jota-sky-text">
        <span className="jota-sky-weekday">{weekdayCap}, {dateStr}</span>
        <span className="jota-sky-clock">{timeStr}</span>
      </div>
    </div>
  );
}


function RecurrenceEditor({ value, onChange }) {
  const rec = value || { type: "diario" };
  const [dateDraft, setDateDraft] = useState("");

  const setType = (type) => {
    if (type === "dias_semana") onChange({ type, diasSemana: (rec.diasSemana && rec.diasSemana.length) ? rec.diasSemana : WEEKDAYS.map((_, i) => i) });
    else if (type === "intervalo") onChange({ type, intervaloDias: rec.intervaloDias || 2, dataInicio: rec.dataInicio || todayKey() });
    else if (type === "datas") onChange({ type, datas: rec.datas || [] });
    else onChange({ type: "diario" });
  };

  const toggleWeekday = (idx) => {
    const set = new Set(rec.diasSemana || []);
    if (set.has(idx)) set.delete(idx); else set.add(idx);
    onChange({ ...rec, diasSemana: Array.from(set).sort() });
  };

  const addDate = () => {
    if (!dateDraft) return;
    const set = new Set(rec.datas || []);
    set.add(dateDraft);
    onChange({ ...rec, datas: Array.from(set).sort() });
    setDateDraft("");
  };

  const removeDate = (d) => {
    onChange({ ...rec, datas: (rec.datas || []).filter((x) => x !== d) });
  };

  const setTime = (t) => {
    if (t) {
      onChange({ ...rec, time: t });
    } else {
      const { time, ...rest } = rec;
      onChange(rest);
    }
  };

  return (
    <div className="jota-recur">
      <div className="jota-recur-chips">
        {RECUR_OPTIONS.map((opt) => (
          <button
            key={opt.type}
            className={`jota-recur-chip ${rec.type === opt.type ? "active" : ""}`}
            onClick={() => setType(opt.type)}
          >
            {opt.label}
          </button>
        ))}
      </div>

      {rec.type !== "diario" && (
        <div className="jota-recur-subpanel">
          {rec.type === "dias_semana" && (
            <div className="jota-weekday-row">
              {WEEKDAY_ORDER.map((idx) => (
                <button
                  key={idx}
                  className={`jota-weekday-dot ${(rec.diasSemana || []).includes(idx) ? "active" : ""}`}
                  onClick={() => toggleWeekday(idx)}
                >
                  {WEEKDAYS[idx][0].toUpperCase()}
                </button>
              ))}
            </div>
          )}

          {rec.type === "intervalo" && (
            <div className="jota-interval-row">
              <span>A cada</span>
              <input
                type="number"
                min="1"
                className="jota-interval-input"
                value={rec.intervaloDias || 2}
                onChange={(e) => onChange({ ...rec, intervaloDias: Math.max(1, parseInt(e.target.value) || 1) })}
              />
              <span>dias, a partir de hoje</span>
            </div>
          )}

          {rec.type === "datas" && (
            <div className="jota-dates-block">
              {(rec.datas || []).length > 0 && (
                <div className="jota-dates-list">
                  {rec.datas.map((d) => (
                    <span key={d} className="jota-date-chip">
                      {formatDateShort(d)}
                      <button onClick={() => removeDate(d)} aria-label="Remover data"><X size={10} strokeWidth={2.5} /></button>
                    </span>
                  ))}
                </div>
              )}
              <div className="jota-date-add-row">
                <input
                  type="date"
                  className="jota-date-input"
                  value={dateDraft}
                  onChange={(e) => setDateDraft(e.target.value)}
                />
                <button className="jota-add-confirm" onClick={addDate} aria-label="Adicionar data">
                  <Plus size={13} strokeWidth={2} />
                </button>
              </div>
            </div>
          )}
        </div>
      )}

      <div className="jota-recur-time">
        <span className="jota-recur-time-left">
          <Clock size={14} strokeWidth={1.9} />
          Horário <span className="jota-recur-time-opt">(opcional)</span>
        </span>
        <span className="jota-recur-time-right">
          <input
            type="time"
            className="jota-recur-time-input"
            value={rec.time || ""}
            onChange={(e) => setTime(e.target.value)}
          />
          {rec.time && (
            <button className="jota-recur-time-clear" onClick={() => setTime("")} aria-label="Limpar horário">
              <X size={12} strokeWidth={2.5} />
            </button>
          )}
        </span>
      </div>
    </div>
  );
}

function RoutineCard({ routine, onToggleStep, onAddStep, onRemoveStep, onMoveStep, onEditRecurrence, onRename, onDeleteRoutine, onMoveRoutine, onToggleCategories, canMoveUp, canMoveDown, onViewProduct, onEditStepInfo, anchorDate, mode = "routine", readOnly = false }) {
  const isCalendar = mode === "calendar";
  const [editMode, setEditMode] = useState(false);
  const [editingName, setEditingName] = useState(false);
  const [nameDraft, setNameDraft] = useState(routine.name);
  const [addingStep, setAddingStep] = useState(false);
  const [productQuery, setProductQuery] = useState("");
  const [selectedProduct, setSelectedProduct] = useState(null);
  const [draftRecurrence, setDraftRecurrence] = useState({ type: "diario" });
  const [draftCategory, setDraftCategory] = useState(null); // categoria do produto sendo adicionado
  // Produto personalizado (não existe no catálogo): nome + miniatura
  const [customMode, setCustomMode] = useState(false);
  const [customName, setCustomName] = useState("");
  const [customIcon, setCustomIcon] = useState("generico");
  // Edição do produto manual já na rotina (lápis): renomear + trocar ícone
  const [editingCustomId, setEditingCustomId] = useState(null);
  const [customEditName, setCustomEditName] = useState("");
  const [customEditIcon, setCustomEditIcon] = useState("generico");

  const openCustomEdit = (step) => {
    setOpenRecurrenceId(null);
    setEditingCustomId(step.id);
    setCustomEditName(step.label);
    setCustomEditIcon(step.photo?.startsWith("icon:") ? step.photo.slice(5) : "generico");
  };
  const saveCustomEdit = (stepId) => {
    const name = customEditName.trim();
    if (!name) return;
    onEditStepInfo(stepId, { label: name, photo: `icon:${customEditIcon}` });
    setEditingCustomId(null);
  };
  const viewProduct = (step) => {
    const prod = PRODUCT_CATALOG.find((p) => p.id === step.productId);
    if (prod) onViewProduct(prod);
  };
  const [openRecurrenceId, setOpenRecurrenceId] = useState(null);
  const [recurDraft, setRecurDraft] = useState(null);   // rascunho da recorrência em edição
  const [scopeModal, setScopeModal] = useState(null);   // { step, draft, timeOnly, desc } | null
  const [confirmDeleteRoutine, setConfirmDeleteRoutine] = useState(false);
  const [confirmRemoveStep, setConfirmRemoveStep] = useState(null); // step sem recorrência real | null
  const [removeScopeModal, setRemoveScopeModal] = useState(null);   // step recorrente | null
  const [dragId, setDragId] = useState(null);
  const stepRefs = useRef({});
  const dragState = useRef({ lastY: 0 });

  // Deslizar o item pra esquerda revela os 3 botões de edição (atalho sem precisar
  // entrar no modo de edição da rotina inteira). Só um item aberto por vez.
  const SWIPE_REVEAL = 116;
  const [swipeOpenId, setSwipeOpenId] = useState(null);
  const swipeState = useRef({ id: null, startX: 0, startY: 0, dx: 0, axis: null });

  const closeSwipe = () => setSwipeOpenId(null);

  const handleSwipeDown = (e, stepId) => {
    if (editMode) return;
    swipeState.current = { id: stepId, startX: e.clientX, startY: e.clientY, dx: 0, axis: null };
  };
  const handleSwipeMove = (e, stepId) => {
    const st = swipeState.current;
    if (st.id !== stepId) return;
    const dx = e.clientX - st.startX;
    const dy = e.clientY - st.startY;
    if (st.axis === null && (Math.abs(dx) > 6 || Math.abs(dy) > 6)) {
      st.axis = Math.abs(dx) > Math.abs(dy) ? "x" : "y";
    }
    if (st.axis !== "x") return;
    const base = swipeOpenId === stepId ? -SWIPE_REVEAL : 0;
    const next = Math.max(-SWIPE_REVEAL, Math.min(0, base + dx));
    st.dx = next;
    const el = stepRefs.current[stepId];
    if (el) el.style.transform = `translateX(${next}px)`;
  };
  const handleSwipeUp = (e, stepId) => {
    const st = swipeState.current;
    if (st.id !== stepId) return;
    const el = stepRefs.current[stepId];
    if (st.axis !== "x") { st.id = null; return; }
    const shouldOpen = st.dx < -SWIPE_REVEAL * 0.4;
    if (el) el.style.transform = shouldOpen ? `translateX(-${SWIPE_REVEAL}px)` : "translateX(0)";
    setSwipeOpenId(shouldOpen ? stepId : null);
    st.id = null;
  };
  useEffect(() => { if (editMode) setSwipeOpenId(null); }, [editMode]);
  // Mantém o transform do DOM em sincronia sempre que o item aberto muda por outro
  // caminho que não o próprio gesto (ex: tocar num botão revelado, ou entrar em editMode).
  useEffect(() => {
    Object.entries(stepRefs.current).forEach(([id, el]) => {
      if (!el) return;
      el.style.transform = id === swipeOpenId ? `translateX(-${SWIPE_REVEAL}px)` : "translateX(0)";
    });
  }, [swipeOpenId]);

  // "today"/"tKey" = a data-âncora do card (hoje na tela Rotina; o dia selecionado no
  // calendário). Toda a lógica de agendamento/escopo já opera sobre essa âncora.
  const today = anchorDate || new Date();
  const tKey = todayKey(today);

  // Abre/fecha o editor de recorrência de um passo (seedando o rascunho com o efetivo de hoje).
  const openRecurrence = (step) => {
    setEditingCustomId(null);
    if (openRecurrenceId === step.id) {
      setOpenRecurrenceId(null);
      setRecurDraft(null);
    } else {
      setOpenRecurrenceId(step.id);
      setRecurDraft(effectiveRecurrence(step, today));
    }
  };
  const closeRecurrence = () => {
    setOpenRecurrenceId(null);
    setRecurDraft(null);
  };
  // "Aplicar": sem mudança fecha; não-recorrente aplica direto; recorrente abre o pop-up de escopo.
  const applyRecurrence = (step) => {
    const seed = effectiveRecurrence(step, today);
    const draft = recurDraft || seed;
    if (!recChanged(seed, draft)) { closeRecurrence(); return; }
    if (!isRecurringStep(step.recurrence)) {
      onEditRecurrence(step.id, applyRecurrenceScoped(step, draft, "all", tKey));
      closeRecurrence();
      return;
    }
    // "Somente este dia" só faz sentido quando o passo realmente ocorre hoje E a mudança
    // é só de horário (decisão 1). No modo de edição a lista mostra passos não-agendados
    // hoje também — pra esses, não oferecer o escopo de dia.
    const dayScope = isTimeOnlyChange(seed, draft) && isScheduledOn(step, today);
    setScopeModal({ step, draft, dayScope, desc: describeRecurrenceChange(seed, draft) });
  };
  const commitScope = (scope) => {
    if (!scopeModal) return;
    onEditRecurrence(scopeModal.step.id, applyRecurrenceScoped(scopeModal.step, scopeModal.draft, scope, tKey));
    setScopeModal(null);
    closeRecurrence();
  };

  // Remover um passo: se ele repete em mais de um dia, pergunta o escopo (igual
  // à edição de frequência); senão só confirma e apaga de vez.
  const requestRemoveStep = (step) => {
    closeSwipe();
    if (isRecurringStep(step.recurrence)) setRemoveScopeModal(step);
    else setConfirmRemoveStep(step);
  };
  const commitRemoveScope = (scope) => {
    if (!removeScopeModal) return;
    const step = removeScopeModal;
    setRemoveScopeModal(null);
    if (scope === "all") { onRemoveStep(step.id); return; }
    onEditRecurrence(step.id, applyRemovalScoped(step, scope, tKey));
  };

  const commitName = () => {
    const trimmed = nameDraft.trim();
    if (trimmed) onRename(trimmed);
    else setNameDraft(routine.name);
    setEditingName(false);
  };

  const resetAddPanel = () => {
    setAddingStep(false);
    setProductQuery("");
    setSelectedProduct(null);
    setDraftRecurrence({ type: "diario" });
    setDraftCategory(null);
    setCustomMode(false);
    setCustomName("");
    setCustomIcon("generico");
  };

  const confirmCustomProduct = () => {
    const name = customName.trim();
    if (!name) return;
    // id null = personalizado do usuário (não entra no catálogo geral).
    // A miniatura vai no campo photo como "icon:<id>" e persiste no banco.
    setSelectedProduct({ id: null, name, photo: `icon:${customIcon}`, brand: null, custom: true });
    setCustomMode(false);
  };

  const commitStep = () => {
    if (!selectedProduct) return;
    if (selectedProduct.custom && !draftCategory) return; // categoria é obrigatória pra produto manual
    onAddStep(selectedProduct.name, draftRecurrence, selectedProduct.photo || null, selectedProduct.id || null, draftCategory);
    resetAddPanel();
  };

  const filteredCatalog = productQuery.trim()
    ? PRODUCT_CATALOG.filter((p) => p.name.toLowerCase().includes(productQuery.trim().toLowerCase()))
    : PRODUCT_CATALOG;

  const handleGripDown = (e, stepId) => {
    e.currentTarget.setPointerCapture(e.pointerId);
    setDragId(stepId);
    dragState.current.lastY = e.clientY;
  };

  const handleGripMove = (e, stepId) => {
    if (dragId !== stepId) return;
    const deltaY = e.clientY - dragState.current.lastY;
    const rowEl = stepRefs.current[stepId];
    const rowHeight = rowEl ? rowEl.offsetHeight : 46;
    if (Math.abs(deltaY) > rowHeight / 2) {
      onMoveStep(stepId, deltaY > 0 ? 1 : -1);
      dragState.current.lastY = e.clientY;
    }
  };

  const handleGripUp = (e, stepId) => {
    if (dragId === stepId) setDragId(null);
  };

  const visibleSteps = editMode
    ? routine.steps
    : routine.steps
        .filter((s) => isScheduledOn(s, today))
        .map((s, i) => ({ s, i }))
        .sort((a, b) => {
          const aDone = !!a.s.completions?.[tKey];
          const bDone = !!b.s.completions?.[tKey];
          if (aDone === bDone) return a.i - b.i;
          return aDone ? 1 : -1;
        })
        .map(({ s }) => s);

  const scheduledToday = routine.steps.filter((s) => isScheduledOn(s, today));
  const todayDoneCount = scheduledToday.filter((s) => s.completions?.[tKey]).length;
  const todayTotal = scheduledToday.length;
  const allDoneToday = todayTotal > 0 && todayDoneCount === todayTotal;

  // Quando a lista de passos muda (adicionar/remover/reordenar), zera qualquer
  // swipe aberto e limpa os transforms imperativos — senão um item recém-inserido
  // pode herdar a "gaveta" aberta de outro e mostrar as ações sem gesto do usuário.
  const visibleStepsKey = visibleSteps.map((s) => s.id).join(",");
  useEffect(() => {
    setSwipeOpenId(null);
    Object.values(stepRefs.current).forEach((el) => { if (el) el.style.transform = "translateX(0)"; });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [visibleStepsKey]);

  // Check da rotina toda: marca (ou desmarca) de uma vez todos os passos do dia,
  // pra pessoa não precisar tocar um por um.
  const toggleAllToday = () => {
    const target = !allDoneToday;
    scheduledToday.forEach((s) => {
      const isDone = !!s.completions?.[tKey];
      if (isDone !== target) onToggleStep(s.id, tKey);
    });
  };

  // Fora do modo de edição, agrupa sutilmente por categoria (ordem de STEP_TYPES;
  // sem categoria reconhecida cai em "Outros" no fim). Em edição mantém a lista
  // plana pra não confundir o arrastar-pra-reordenar.
  const renderItems = editMode || routine.hideCategories
    ? visibleSteps.map((step) => ({ step }))
    : (() => {
        const byCategory = new Map();
        visibleSteps.forEach((step) => {
          const key = step.category && STEP_TYPES.includes(step.category) ? step.category : "__outros__";
          if (!byCategory.has(key)) byCategory.set(key, []);
          byCategory.get(key).push(step);
        });
        const realKeys = STEP_TYPES.filter((c) => byCategory.has(c));
        // Se nenhum passo tem categoria reconhecida, mantém a lista plana (sem
        // um "Outros" solitário). Com pelo menos uma categoria de verdade, o
        // rótulo sempre aparece — mesmo que a categoria tenha um único produto.
        if (realKeys.length === 0) return visibleSteps.map((step) => ({ step }));
        const orderedKeys = byCategory.has("__outros__") ? [...realKeys, "__outros__"] : realKeys;
        const items = [];
        orderedKeys.forEach((key) => {
          items.push({ header: key === "__outros__" ? "Outros" : key });
          byCategory.get(key).forEach((step) => items.push({ step }));
        });
        return items;
      })();

  return (
    <div className={`jota-card ${editMode ? "editing" : ""}`}>
      <div className="jota-card-head">
        <span className="jota-card-head-left">
          {!readOnly && !editMode && todayTotal > 0 && (
            <button
              className={`jota-check-all-btn ${allDoneToday ? "checked" : ""}`}
              onClick={toggleAllToday}
              aria-label={allDoneToday ? "Desmarcar todos os passos" : "Marcar todos os passos como feitos"}
            >
              <Check size={11} strokeWidth={3} />
            </button>
          )}
          {editMode && editingName && !isCalendar ? (
            <input
              className="jota-rename-input"
              value={nameDraft}
              autoFocus
              onChange={(e) => setNameDraft(e.target.value)}
              onBlur={commitName}
              onKeyDown={(e) => {
                if (e.key === "Enter") commitName();
                if (e.key === "Escape") { setNameDraft(routine.name); setEditingName(false); }
              }}
            />
          ) : editMode && !isCalendar ? (
            <span className="jota-card-title jota-card-title-editable" onClick={() => setEditingName(true)}>
              {routine.name}
              <Pencil size={11} strokeWidth={1.75} className="jota-rename-icon" />
            </span>
          ) : (
            <span className="jota-card-title">{routine.name}</span>
          )}
        </span>
        <span className="jota-card-head-right">
          <span className="jota-card-count">{todayDoneCount}/{todayTotal}</span>
          {editMode && !isCalendar && (
            <>
              {onToggleCategories && (
                <button
                  className={`jota-icon-btn ${routine.hideCategories ? "" : "active"}`}
                  onClick={onToggleCategories}
                  aria-label={routine.hideCategories ? "Mostrar categorias" : "Ocultar categorias"}
                  title={routine.hideCategories ? "Mostrar categorias" : "Ocultar categorias"}
                >
                  {routine.hideCategories ? <EyeOff size={14} strokeWidth={1.75} /> : <Eye size={14} strokeWidth={1.75} />}
                </button>
              )}
              <button className="jota-icon-btn" onClick={() => onMoveRoutine(-1)} disabled={!canMoveUp} aria-label="Mover rotina pra cima">
                <ChevronUp size={14} strokeWidth={1.75} />
              </button>
              <button className="jota-icon-btn" onClick={() => onMoveRoutine(1)} disabled={!canMoveDown} aria-label="Mover rotina pra baixo">
                <ChevronDown size={14} strokeWidth={1.75} />
              </button>
              <button className="jota-icon-btn" onClick={() => setConfirmDeleteRoutine(true)} aria-label="Remover rotina">
                <Trash2 size={13} strokeWidth={1.75} />
              </button>
            </>
          )}
          {!readOnly && (
            <button
              className={`jota-icon-btn ${editMode ? "active" : ""}`}
              onClick={() => setEditMode((v) => !v)}
              aria-label="Editar rotina"
            >
              <Settings size={14} strokeWidth={1.75} />
            </button>
          )}
        </span>
      </div>

      {visibleSteps.length === 0 && (
        <div className="jota-empty-steps">{isCalendar ? "Nada agendado nesse dia nessa rotina." : "Nada agendado pra hoje nessa rotina."}</div>
      )}

      {renderItems.map((item, idx) => {
        if (item.header) {
          return <div key={`cat-${item.header}-${idx}`} className="jota-step-category-label">{item.header}</div>;
        }
        const step = item.step;
        const effRec = effectiveRecurrence(step, today);
        const label = recurrenceLabel(effRec);
        const effTime = effRec.time || null;
        const doneToday = !!step.completions?.[tKey];
        return (
          <div key={step.id} className="jota-step-block">
            <div className="jota-swipe-wrap">
              {!editMode && (
                <span className="jota-swipe-actions">
                  {step.productId && (
                    <button
                      className="jota-swipe-action"
                      onClick={() => { closeSwipe(); viewProduct(step); }}
                      aria-label="Ver produto"
                    >
                      <Eye size={15} strokeWidth={2} />
                    </button>
                  )}
                  {!step.productId && (
                    <button
                      className="jota-swipe-action"
                      onClick={() => { closeSwipe(); openCustomEdit(step); }}
                      aria-label="Renomear e trocar miniatura"
                    >
                      <Pencil size={15} strokeWidth={2} />
                    </button>
                  )}
                  <button
                    className="jota-swipe-action"
                    onClick={() => { closeSwipe(); openRecurrence(step); }}
                    aria-label="Editar frequência e horário"
                  >
                    <Repeat size={15} strokeWidth={2} />
                  </button>
                  <button className="jota-swipe-action" onClick={() => requestRemoveStep(step)} aria-label="Remover produto">
                    <Trash2 size={15} strokeWidth={2} />
                  </button>
                </span>
              )}
              <div
                className={`jota-item ${dragId === step.id ? "dragging" : ""}`}
                ref={(el) => { stepRefs.current[step.id] = el; }}
                onPointerDown={(e) => handleSwipeDown(e, step.id)}
                onPointerMove={(e) => handleSwipeMove(e, step.id)}
                onPointerUp={(e) => handleSwipeUp(e, step.id)}
                onPointerCancel={(e) => handleSwipeUp(e, step.id)}
              >
                {editMode && (
                  <button
                    className="jota-drag-handle"
                    onPointerDown={(e) => handleGripDown(e, step.id)}
                    onPointerMove={(e) => handleGripMove(e, step.id)}
                    onPointerUp={(e) => handleGripUp(e, step.id)}
                    onPointerCancel={(e) => handleGripUp(e, step.id)}
                    aria-label="Arrastar para reordenar"
                  >
                    <GripVertical size={14} strokeWidth={1.75} />
                  </button>
                )}
                <span className={`jota-checkbox ${doneToday ? "checked" : ""}`} onClick={() => onToggleStep(step.id, tKey)}>
                  {doneToday && <Check size={11} color="#fff" strokeWidth={3} />}
                </span>
                <ProductThumb name={step.label} photo={step.photo} size={26} />
                <span className="jota-item-label-col" onClick={() => onToggleStep(step.id, tKey)}>
                  <span className={`jota-item-label ${doneToday ? "checked" : ""}`}>{step.label}</span>
                  <span className="jota-item-meta">
                    {effTime && (
                      <span className="jota-time-badge"><Clock size={10} strokeWidth={2.2} /> {effTime}</span>
                    )}
                    {label && <span className="jota-recur-badge">{label}</span>}
                  </span>
                </span>
                {editMode && (
                  <span className="jota-item-actions">
                    {!step.productId && (
                      <button
                        className="jota-item-action"
                        onClick={() => (editingCustomId === step.id ? setEditingCustomId(null) : openCustomEdit(step))}
                        aria-label="Renomear e trocar miniatura"
                      >
                        <Pencil size={13} strokeWidth={2} />
                      </button>
                    )}
                    <button
                      className="jota-item-action"
                      onClick={() => openRecurrence(step)}
                      aria-label="Editar frequência e horário"
                    >
                      <Repeat size={13} strokeWidth={2} />
                    </button>
                    <button className="jota-item-action" onClick={() => requestRemoveStep(step)} aria-label="Remover produto">
                      <Trash2 size={13} strokeWidth={2} />
                    </button>
                  </span>
                )}
              </div>
            </div>
            {openRecurrenceId === step.id && (
              <div className="jota-recur-edit">
                <RecurrenceEditor
                  value={recurDraft || step.recurrence}
                  onChange={setRecurDraft}
                />
                <div className="jota-add-footer">
                  <button className="jota-add-cancel-btn" onClick={closeRecurrence}>Cancelar</button>
                  <button className="jota-add-save-btn" onClick={() => applyRecurrence(step)}>
                    <Check size={14} strokeWidth={2.5} /> Aplicar
                  </button>
                </div>
              </div>
            )}
            {editingCustomId === step.id && (
              <div className="jota-custom-product jota-custom-edit">
                <span className="jota-custom-label">Nome do produto</span>
                <input
                  className="jota-custom-name-input"
                  value={customEditName}
                  maxLength={60}
                  onChange={(e) => setCustomEditName(e.target.value)}
                  onKeyDown={(e) => e.key === "Enter" && saveCustomEdit(step.id)}
                />
                <span className="jota-custom-counter">{customEditName.length}/60</span>
                <span className="jota-custom-label">Miniatura</span>
                <div className="jota-custom-icon-grid">
                  {CUSTOM_PRODUCT_ICONS.map((ic) => (
                    <button
                      key={ic.id}
                      className={`jota-custom-icon-option ${customEditIcon === ic.id ? "selected" : ""}`}
                      onClick={() => setCustomEditIcon(ic.id)}
                      aria-label={ic.label}
                      title={ic.label}
                    >
                      <CustomProductIcon type={ic.id} size={24} />
                    </button>
                  ))}
                </div>
                <div className="jota-add-footer">
                  <button className="jota-add-cancel-btn" onClick={() => setEditingCustomId(null)}>Cancelar</button>
                  <button className="jota-add-save-btn" onClick={() => saveCustomEdit(step.id)} disabled={!customEditName.trim()}>
                    <Check size={14} strokeWidth={2.5} /> Salvar
                  </button>
                </div>
              </div>
            )}
          </div>
        );
      })}

      {/* Adicionar produto fica visível sempre — não só no modo de edição (menos em dia read-only) */}
      {!readOnly && (
        addingStep ? (
          <div className="jota-add-block">
            {!selectedProduct && customMode ? (
              <div className="jota-custom-product">
                <span className="jota-custom-label">Nome do produto</span>
                <input
                  className="jota-custom-name-input"
                  placeholder="Ex: Meu sérum favorito"
                  value={customName}
                  maxLength={60}
                  autoFocus
                  onChange={(e) => setCustomName(e.target.value)}
                  onKeyDown={(e) => e.key === "Enter" && confirmCustomProduct()}
                />
                <span className="jota-custom-counter">{customName.length}/60</span>

                <span className="jota-custom-label">Escolha uma miniatura</span>
                <div className="jota-custom-icon-grid">
                  {CUSTOM_PRODUCT_ICONS.map((ic) => (
                    <button
                      key={ic.id}
                      className={`jota-custom-icon-option ${customIcon === ic.id ? "selected" : ""}`}
                      onClick={() => setCustomIcon(ic.id)}
                      aria-label={ic.label}
                      title={ic.label}
                    >
                      <CustomProductIcon type={ic.id} size={24} />
                    </button>
                  ))}
                </div>

                <div className="jota-add-footer">
                  <button className="jota-add-cancel-btn" onClick={() => setCustomMode(false)}>Voltar</button>
                  <button className="jota-add-save-btn" onClick={confirmCustomProduct} disabled={!customName.trim()}>
                    <Check size={14} strokeWidth={2.5} /> Continuar
                  </button>
                </div>
              </div>
            ) : !selectedProduct ? (
              <div className="jota-product-picker">
                <input
                  className="jota-product-search"
                  placeholder="Pesquise ou adicione um produto"
                  value={productQuery}
                  autoFocus
                  onChange={(e) => setProductQuery(e.target.value)}
                />
                <div className="jota-product-list">
                  <button
                    className="jota-product-option jota-product-custom"
                    onClick={() => { setCustomName(productQuery.trim()); setCustomMode(true); }}
                  >
                    <span className="jota-product-custom-plus"><Plus size={15} strokeWidth={2.5} /></span>
                    {productQuery.trim()
                      ? `Adicionar "${productQuery.trim()}" manualmente`
                      : "Adicionar produto manualmente"}
                  </button>
                  {filteredCatalog.map((p) => (
                    <button key={p.id} className="jota-product-option" onClick={() => { setSelectedProduct(p); setDraftCategory(p.stepType || null); }}>
                      <ProductThumb name={p.name} photo={p.photo} size={28} />
                      <span className="jota-product-option-text">
                        <span className="jota-product-option-name">{p.name}</span>
                        <span className="jota-product-option-brand">
                          {p.brand}
                          {p.rating && (
                            <span className="jota-product-rating">
                              <Star size={10} strokeWidth={0} fill="currentColor" /> {p.rating.toFixed(1)}
                            </span>
                          )}
                        </span>
                        {p.concerns?.length > 0 && (
                          <span className="jota-product-concerns">{p.concerns.join(" · ")}</span>
                        )}
                      </span>
                    </button>
                  ))}
                  {productQuery.trim() && filteredCatalog.length === 0 && (
                    <div className="jota-empty-steps">Nenhum produto no catálogo com esse nome — use o "Adicionar manualmente" acima.</div>
                  )}
                </div>
                <button className="jota-add-cancel" onClick={resetAddPanel}>Cancelar</button>
              </div>
            ) : (
              <>
                <div className="jota-product-chosen">
                  <ProductThumb name={selectedProduct.name} photo={selectedProduct.photo} size={30} />
                  <span className="jota-product-chosen-name">{selectedProduct.name}</span>
                  <button className="jota-product-swap" onClick={() => setSelectedProduct(null)}>trocar</button>
                </div>
                {selectedProduct.custom ? (
                  <div className="jota-category-picker">
                    <span className="jota-custom-label">Categoria</span>
                    <select
                      className="jota-category-select"
                      value={draftCategory || ""}
                      onChange={(e) => setDraftCategory(e.target.value || null)}
                    >
                      <option value="" disabled>Escolha uma categoria</option>
                      {STEP_TYPES.map((t) => <option key={t} value={t}>{t}</option>)}
                    </select>
                  </div>
                ) : draftCategory && (
                  <div className="jota-category-picker">
                    <span className="jota-custom-label">Categoria</span>
                    <span className="jota-category-tag">{draftCategory}</span>
                  </div>
                )}
                <RecurrenceEditor value={draftRecurrence} onChange={setDraftRecurrence} />
                <div className="jota-add-footer">
                  <button className="jota-add-cancel-btn" onClick={resetAddPanel}>Cancelar</button>
                  <button
                    className="jota-add-save-btn"
                    onClick={commitStep}
                    disabled={selectedProduct.custom && !draftCategory}
                  >
                    <Check size={14} strokeWidth={2.5} /> Adicionar
                  </button>
                </div>
              </>
            )}
          </div>
        ) : (
          <button className="jota-add-step-btn" onClick={() => setAddingStep(true)}>
            <Plus size={14} strokeWidth={2} /> Adicionar produto
          </button>
        )
      )}

      {editMode && !editingCustomId && (
        <div className="jota-routine-footer">
          {!isCalendar && (
            <button className="jota-routine-delete-btn" onClick={() => setConfirmDeleteRoutine(true)}>
              <Trash2 size={13} strokeWidth={1.75} /> Excluir rotina
            </button>
          )}
          <button
            className="jota-routine-save-btn"
            onClick={() => { setEditMode(false); setEditingName(false); setOpenRecurrenceId(null); setRecurDraft(null); }}
          >
            <Check size={14} strokeWidth={2} /> {isCalendar ? "Concluir edição" : "Salvar rotina"}
          </button>
        </div>
      )}

      {scopeModal && (
        <div className="jota-scope-overlay" onClick={() => setScopeModal(null)}>
          <div className="jota-scope-card" onClick={(e) => e.stopPropagation()}>
            <div className="jota-scope-title">Alterar programação</div>
            <div className="jota-scope-sub">
              <strong>{scopeModal.step.label}</strong>
              {scopeModal.desc ? <> — {scopeModal.desc}</> : null}
            </div>
            <div className="jota-scope-options">
              {scopeModal.dayScope && (
                <button className="jota-scope-opt" onClick={() => commitScope("day")}>
                  <span className="jota-scope-opt-title">Alterar somente este dia</span>
                  <span className="jota-scope-opt-desc">Muda só este dia. Os outros dias continuam como estão.</span>
                </button>
              )}
              <button className="jota-scope-opt" onClick={() => commitScope("forward")}>
                <span className="jota-scope-opt-title">Alterar este dia e os próximos</span>
                <span className="jota-scope-opt-desc">Passa a valer deste dia em diante. Os dias anteriores não mudam.</span>
              </button>
              <button className="jota-scope-opt" onClick={() => commitScope("all")}>
                <span className="jota-scope-opt-title">Alterar toda a programação</span>
                <span className="jota-scope-opt-desc">Redefine a programação inteira e limpa exceções anteriores.</span>
              </button>
            </div>
            <button className="jota-scope-cancel" onClick={() => setScopeModal(null)}>Cancelar</button>
          </div>
        </div>
      )}

      {removeScopeModal && (
        <div className="jota-scope-overlay" onClick={() => setRemoveScopeModal(null)}>
          <div className="jota-scope-card" onClick={(e) => e.stopPropagation()}>
            <div className="jota-scope-title">Remover produto</div>
            <div className="jota-scope-sub">
              <strong>{removeScopeModal.label}</strong>
            </div>
            <div className="jota-scope-options">
              {isScheduledOn(removeScopeModal, today) && (
                <button className="jota-scope-opt" onClick={() => commitRemoveScope("day")}>
                  <span className="jota-scope-opt-title">Remover somente hoje</span>
                  <span className="jota-scope-opt-desc">Some só de hoje. Os outros dias continuam como estão.</span>
                </button>
              )}
              <button className="jota-scope-opt" onClick={() => commitRemoveScope("forward")}>
                <span className="jota-scope-opt-title">Remover deste dia em diante</span>
                <span className="jota-scope-opt-desc">Para de aparecer a partir de hoje. O histórico de dias anteriores fica registrado.</span>
              </button>
              <button className="jota-scope-opt" onClick={() => commitRemoveScope("all")}>
                <span className="jota-scope-opt-title">Remover de toda a rotina</span>
                <span className="jota-scope-opt-desc">Apaga o produto da rotina inteira, incluindo o histórico. Não pode ser desfeito.</span>
              </button>
            </div>
            <button className="jota-scope-cancel" onClick={() => setRemoveScopeModal(null)}>Cancelar</button>
          </div>
        </div>
      )}

      {confirmDeleteRoutine && (
        <div className="jota-honesty-overlay" onClick={() => setConfirmDeleteRoutine(false)}>
          <div className="jota-honesty-card" onClick={(e) => e.stopPropagation()}>
            <span className="jota-honesty-title">Excluir rotina?</span>
            <p className="jota-honesty-text">
              Isso remove <strong>{routine.name}</strong> e todos os passos dela. Essa ação não pode ser desfeita.
            </p>
            <button
              className="jota-review-submit"
              onClick={() => { setConfirmDeleteRoutine(false); onDeleteRoutine(); }}
            >
              Excluir rotina
            </button>
            <button className="jota-honesty-secondary" onClick={() => setConfirmDeleteRoutine(false)}>
              Cancelar
            </button>
          </div>
        </div>
      )}

      {confirmRemoveStep && (
        <div className="jota-honesty-overlay" onClick={() => setConfirmRemoveStep(null)}>
          <div className="jota-honesty-card" onClick={(e) => e.stopPropagation()}>
            <span className="jota-honesty-title">Remover produto?</span>
            <p className="jota-honesty-text">
              <strong>{confirmRemoveStep.label}</strong> vai sair dessa rotina.
            </p>
            <button
              className="jota-review-submit"
              onClick={() => { const s = confirmRemoveStep; setConfirmRemoveStep(null); onRemoveStep(s.id); }}
            >
              Remover
            </button>
            <button className="jota-honesty-secondary" onClick={() => setConfirmRemoveStep(null)}>
              Cancelar
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

function AddRoutineButton({ onAdd }) {
  const [adding, setAdding] = useState(false);
  const [draft, setDraft] = useState("");
  const presets = ["Manhã", "Tarde", "Noite"];

  const commit = (name) => {
    const trimmed = (name ?? draft).trim();
    if (trimmed) onAdd(trimmed);
    setDraft("");
    setAdding(false);
  };

  if (!adding) {
    return (
      <button className="jota-add-routine-btn" onClick={() => setAdding(true)}>
        <Plus size={15} strokeWidth={2} /> Nova rotina
      </button>
    );
  }

  return (
    <div className="jota-card jota-add-routine-card">
      <input
        className="jota-add-input"
        placeholder="Nome da rotina (ex: Tarde)"
        value={draft}
        autoFocus
        onChange={(e) => setDraft(e.target.value)}
        onKeyDown={(e) => {
          if (e.key === "Enter") commit();
          if (e.key === "Escape") { setDraft(""); setAdding(false); }
        }}
      />
      <div className="jota-preset-row">
        {presets.map((p) => (
          <button key={p} className="jota-preset-chip" onClick={() => commit(p)}>{p}</button>
        ))}
        <button className="jota-add-confirm" onClick={() => commit()} aria-label="Confirmar">
          <Check size={14} strokeWidth={2} />
        </button>
      </div>
    </div>
  );
}

function findTodayReminder(routines) {
  const now = new Date();
  const hour = now.getHours();
  const nowMin = hour * 60 + now.getMinutes();
  const tKey = todayKey(now);

  const pending = [];
  for (const r of routines) {
    for (const s of r.steps) {
      if (!isScheduledOn(s, now)) continue;
      if (s.completions?.[tKey]) continue;
      pending.push({ routine: r, step: s });
    }
  }

  // 1) Prioridade: passos com horário definido pra hoje. Mostra o próximo que
  // ainda vai acontecer; se todos já passaram, o primeiro do dia (atrasado).
  const timed = pending
    .map((p) => ({ ...p, time: effectiveTime(p.step, now) }))
    .filter((p) => p.time)
    .map((p) => ({ ...p, min: timeToMinutes(p.time) }))
    .sort((a, b) => a.min - b.min);
  if (timed.length) {
    const upcoming = timed.find((p) => p.min >= nowMin);
    const chosen = upcoming || timed[0];
    return { type: "timed", routine: chosen.routine, step: chosen.step, overdue: !upcoming, time: chosen.time };
  }

  // 2) Sem horário: heurística de protetor solar de dia, senão o 1º pendente.
  const sunscreenPending = pending.find((p) => /protetor solar/i.test(p.step.label));
  if (hour >= 6 && hour < 18 && sunscreenPending) {
    return { type: "sunscreen", ...sunscreenPending };
  }
  if (pending[0]) {
    return { type: "next", ...pending[0] };
  }
  return { type: "done" };
}

function TodayReminderCard({ routines, onToggleStep }) {
  const reminder = findTodayReminder(routines);

  if (reminder.type === "done") {
    return (
      <div className="jota-card jota-reminder-card jota-reminder-done">
        <span className="jota-reminder-icon">
          <Check size={25} strokeWidth={2.3} />
        </span>
        <div className="jota-reminder-text">
          <span className="jota-reminder-title">Rotina de hoje completa!</span>
          <span className="jota-reminder-sub">Volte amanhã pra continuar sua sequência.</span>
        </div>
      </div>
    );
  }

  const { routine, step, type } = reminder;
  const title = type === "sunscreen" ? "Não esqueça do protetor solar" : step.label;
  const sub = type === "sunscreen"
    ? `Faz parte da sua rotina ${routine.name}`
    : type === "timed"
    ? `${reminder.overdue ? "Estava marcado pra" : "Às"} ${reminder.time} · ${routine.name}`
    : `Próximo passo · ${routine.name}`;

  return (
    <div className="jota-card jota-reminder-card">
      <span className="jota-reminder-icon">
        {type === "sunscreen" ? <Sun size={25} strokeWidth={1.9} /> : type === "timed" ? <Clock size={24} strokeWidth={1.9} /> : <Bell size={23} strokeWidth={1.9} />}
      </span>
      <div className="jota-reminder-text">
        <span className="jota-reminder-title">{title}</span>
        <span className="jota-reminder-sub">{sub}</span>
      </div>
      <button className="jota-reminder-check" onClick={() => onToggleStep(routine.id, step.id)} aria-label="Marcar como feito">
        <Check size={16} strokeWidth={2.5} />
      </button>
    </div>
  );
}

// Lista de RoutineCards compartilhada pela tela Rotina (âncora = hoje) e pelo Calendário
// (âncora = dia selecionado). Mesma fonte → os dois lugares nunca divergem. O onToggleStep
// repassa o dateKey (a âncora) pra conclusão cair no dia certo.
function RoutineCardList({ routines, anchorDate, mode = "routine", readOnly = false, onToggleStep, onAddStep, onRemoveStep, onMoveStep, onEditRecurrence, onRenameRoutine, onDeleteRoutine, onMoveRoutine, onToggleCategories, onViewProduct, onEditStepInfo }) {
  return routines.map((r, i) => (
    <RoutineCard
      key={r.id}
      routine={r}
      anchorDate={anchorDate}
      mode={mode}
      readOnly={readOnly}
      onToggleStep={(stepId, dateKey) => onToggleStep(r.id, stepId, dateKey)}
      onAddStep={(label, recurrence, photo, productId, category) => onAddStep(r.id, label, recurrence, photo, productId, category)}
      onRemoveStep={(stepId) => onRemoveStep(r.id, stepId)}
      onMoveStep={(stepId, dir) => onMoveStep(r.id, stepId, dir)}
      onEditRecurrence={(stepId, recurrence) => onEditRecurrence(r.id, stepId, recurrence)}
      onRename={onRenameRoutine ? (name) => onRenameRoutine(r.id, name) : undefined}
      onDeleteRoutine={onDeleteRoutine ? () => onDeleteRoutine(r.id) : undefined}
      onMoveRoutine={onMoveRoutine ? (dir) => onMoveRoutine(r.id, dir) : undefined}
      onToggleCategories={onToggleCategories ? () => onToggleCategories(r.id) : undefined}
      canMoveUp={i > 0}
      canMoveDown={i < routines.length - 1}
      onViewProduct={onViewProduct}
      onEditStepInfo={(stepId, info) => onEditStepInfo(r.id, stepId, info)}
    />
  ));
}

// Grade de calendário (semana ou mês) embutida na tela de Rotina. O modo
// (semana/mês) vem do segmented control Dia·Semana·Mês que fica acima; aqui só
// mostramos a navegação + a grade de datas pra escolher o dia. Escolher um dia
// muda a "Rotina de <dia>" mostrada logo abaixo.
function RoutineCalendarBlock({ routines, view, refDate, onChangeRefDate, selectedKey, onSelectKey }) {
  const goPrev = () => {
    const d = new Date(refDate);
    if (view === "week") d.setDate(d.getDate() - 7);
    else d.setMonth(d.getMonth() - 1);
    onChangeRefDate(d);
  };
  const goNext = () => {
    const d = new Date(refDate);
    if (view === "week") d.setDate(d.getDate() + 7);
    else d.setMonth(d.getMonth() + 1);
    onChangeRefDate(d);
  };

  const weekDates = getWeekDates(refDate);
  const monthWeeks = view === "month" ? getMonthGrid(refDate) : null;
  const tKey = todayKey();

  const periodLabel = view === "week"
    ? `${weekDates[0].toLocaleDateString("pt-BR", { day: "2-digit", month: "short" })} – ${weekDates[6].toLocaleDateString("pt-BR", { day: "2-digit", month: "short" })}`
    : capitalize(refDate.toLocaleDateString("pt-BR", { month: "long", year: "numeric" }));

  return (
    <div className="jota-rotina-cal-card">
      <div className="jota-cal-nav">
        <button className="jota-icon-btn" onClick={goPrev} aria-label="Anterior">
          <ChevronLeft size={16} strokeWidth={1.75} />
        </button>
        <span className="jota-cal-period">{periodLabel}</span>
        <button className="jota-icon-btn" onClick={goNext} aria-label="Próximo">
          <ChevronRight size={16} strokeWidth={1.75} />
        </button>
      </div>

      {view === "week" && (
        <div className="jota-cal-week-row">
          {weekDates.map((d) => {
            const key = todayKey(d);
            const count = stepsScheduledOnDate(routines, d).length;
            return (
              <button
                key={key}
                className={`jota-cal-day ${key === selectedKey ? "selected" : ""} ${key === tKey ? "today" : ""}`}
                onClick={() => onSelectKey(key)}
              >
                <span className="jota-cal-day-wd">{WEEKDAYS[d.getDay()]}</span>
                <span className="jota-cal-day-num">{d.getDate()}</span>
                {count > 0 && <span className="jota-cal-dot" />}
              </button>
            );
          })}
        </div>
      )}

      {view === "month" && (
        <div className="jota-cal-month">
          <div className="jota-cal-month-head">
            {WEEKDAY_ORDER.map((idx) => <span key={idx}>{WEEKDAYS[idx][0].toUpperCase()}</span>)}
          </div>
          {monthWeeks.map((week, wi) => (
            <div key={wi} className="jota-cal-month-row">
              {week.map((d) => {
                const key = todayKey(d);
                const count = stepsScheduledOnDate(routines, d).length;
                const inMonth = d.getMonth() === refDate.getMonth();
                return (
                  <button
                    key={key}
                    className={`jota-cal-cell ${key === selectedKey ? "selected" : ""} ${key === tKey ? "today" : ""} ${!inMonth ? "muted" : ""}`}
                    onClick={() => onSelectKey(key)}
                  >
                    <span className="jota-cal-cell-num">{d.getDate()}</span>
                    {count > 0 && <span className="jota-cal-dot" />}
                  </button>
                );
              })}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function RotinaScreen({ routines, onToggleStep, onAddStep, onRemoveStep, onMoveStep, onEditRecurrence, onRenameRoutine, onDeleteRoutine, onAddRoutine, onMoveRoutine, onToggleCategories, onViewProduct, onEditStepInfo }) {
  // Modo da tela: "dia" (rotina de hoje, sem calendário), "semana" ou "mes"
  // (calendário com datas pra escolher; a lista de baixo segue o dia selecionado).
  const [mode, setMode] = useState("dia");
  const [calRefDate, setCalRefDate] = useState(new Date());
  const [selectedKey, setSelectedKey] = useState(todayKey());

  const changeMode = (m) => {
    setMode(m);
    if (m === "dia") { setSelectedKey(todayKey()); setCalRefDate(new Date()); }
  };

  // Em "dia" a lista é sempre hoje; em semana/mês segue o dia escolhido no calendário.
  const activeKey = mode === "dia" ? todayKey() : selectedKey;
  const selectedDate = new Date(activeKey + "T00:00:00");
  const isToday = activeKey === todayKey();
  const readOnly = stripTime(selectedDate) < stripTime(new Date());
  const hasAnyStep = routines.some((r) => r.steps.length > 0);

  return (
    <>
      <SkyBanner />

      <TodayReminderCard routines={routines} onToggleStep={onToggleStep} />

      <div className="jota-cal-toggle jota-rotina-mode-toggle">
        <button className={`jota-cal-toggle-btn ${mode === "dia" ? "active" : ""}`} onClick={() => changeMode("dia")}>Dia</button>
        <button className={`jota-cal-toggle-btn ${mode === "semana" ? "active" : ""}`} onClick={() => changeMode("semana")}>Semana</button>
        <button className={`jota-cal-toggle-btn ${mode === "mes" ? "active" : ""}`} onClick={() => changeMode("mes")}>Mês</button>
      </div>

      {mode !== "dia" && (
        <RoutineCalendarBlock
          routines={routines}
          view={mode === "mes" ? "month" : "week"}
          refDate={calRefDate}
          onChangeRefDate={setCalRefDate}
          selectedKey={selectedKey}
          onSelectKey={setSelectedKey}
        />
      )}

      <div className="jota-diario-section-title">
        {isToday ? "Rotina do Dia" : `Rotina de ${formatDateLong(selectedDate)}`}
        {readOnly && <span className="jota-cal-readonly-hint"> Dias passados não podem ser editados</span>}
      </div>

      {isToday ? (
        <RoutineCardList
          routines={routines}
          onToggleStep={onToggleStep}
          onAddStep={onAddStep}
          onRemoveStep={onRemoveStep}
          onMoveStep={onMoveStep}
          onEditRecurrence={onEditRecurrence}
          onRenameRoutine={onRenameRoutine}
          onDeleteRoutine={onDeleteRoutine}
          onMoveRoutine={onMoveRoutine}
          onToggleCategories={onToggleCategories}
          onViewProduct={onViewProduct}
          onEditStepInfo={onEditStepInfo}
        />
      ) : !hasAnyStep ? (
        <div className="jota-empty-steps">Nada agendado nesse dia.</div>
      ) : (
        <RoutineCardList
          routines={routines}
          anchorDate={selectedDate}
          mode="calendar"
          readOnly={readOnly}
          onToggleStep={onToggleStep}
          onAddStep={onAddStep}
          onRemoveStep={onRemoveStep}
          onMoveStep={onMoveStep}
          onEditRecurrence={onEditRecurrence}
          onViewProduct={onViewProduct}
          onEditStepInfo={onEditStepInfo}
        />
      )}

      {isToday && <AddRoutineButton onAdd={onAddRoutine} />}
    </>
  );
}

const PERIOD_LABEL = { dia: "Uso de dia", tarde: "Uso à tarde", noite: "Uso de noite", ambos: "Dia e noite" };

function PriceRangeSlider({ min, max, value, onChange, step = 5 }) {
  const trackRef = useRef(null);
  const dragging = useRef(null);

  const pctFor = (v) => ((v - min) / (max - min)) * 100;

  const valueFromClientX = (clientX) => {
    const rect = trackRef.current.getBoundingClientRect();
    const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
    const raw = min + ratio * (max - min);
    return Math.round(raw / step) * step;
  };

  const startDrag = (which) => (e) => {
    e.currentTarget.setPointerCapture(e.pointerId);
    dragging.current = which;
  };

  const onDrag = (e) => {
    if (!dragging.current) return;
    const v = valueFromClientX(e.clientX);
    if (dragging.current === "min") onChange([Math.min(v, value[1]), value[1]]);
    else onChange([value[0], Math.max(v, value[0])]);
  };

  const endDrag = () => { dragging.current = null; };

  return (
    <div className="jota-price-slider">
      <div className="jota-price-values">
        <span>R$ {value[0]}</span>
        <span>R$ {value[1]}{value[1] >= max ? "+" : ""}</span>
      </div>
      <div className="jota-price-track" ref={trackRef}>
        <div
          className="jota-price-fill"
          style={{ left: `${pctFor(value[0])}%`, width: `${pctFor(value[1]) - pctFor(value[0])}%` }}
        />
        <button
          className="jota-price-thumb"
          style={{ left: `${pctFor(value[0])}%` }}
          onPointerDown={startDrag("min")}
          onPointerMove={onDrag}
          onPointerUp={endDrag}
          onPointerCancel={endDrag}
          aria-label="Preço mínimo"
        />
        <button
          className="jota-price-thumb"
          style={{ left: `${pctFor(value[1])}%` }}
          onPointerDown={startDrag("max")}
          onPointerMove={onDrag}
          onPointerUp={endDrag}
          onPointerCancel={endDrag}
          aria-label="Preço máximo"
        />
      </div>
    </div>
  );
}

// Ícone próprio de nécessaire (bolsinha com zíper) — nenhum ícone do lucide
// tem essa cara; desenhado no mesmo estilo dos CUSTOM_PRODUCT_ICONS (traço
// 1.5, viewBox 24). O fill só se aplica ao corpo da bolsa, pro traço do
// zíper continuar visível mesmo preenchido (estado "marcado").
function NecessaireIcon({ size = 18, strokeWidth = 1.75, fill = "none" }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round">
      <rect x="3.5" y="7.5" width="17" height="13" rx="4" fill={fill} />
      <path d="M8 7.5V4.8a1.2 1.2 0 0 1 1.2-1.2h5.6a1.2 1.2 0 0 1 1.2 1.2V7.5" />
      <path d="M3.5 11.8h17" />
    </svg>
  );
}

// Botões de Desejos/Necessaire/Testado — sempre ao lado de onde o coração de
// favorito aparece. "variant=corner" empilha ícones pequenos (grid do
// ProductCard); "variant=row" usa o mesmo estilo de jota-round-action (coluna
// vertical no ProductDetailSheet e no RateProductsSheet).
function ProductTagActions({ productId, isWishlist, onToggleWishlist, isNecessaire, onToggleNecessaire, status, onCycleStatus, variant = "row" }) {
  const btnClass = variant === "corner" ? "jota-tag-btn" : "jota-round-action";
  const iconSize = variant === "corner" ? 12 : 16;
  const statusLabel = status === "aprovado" ? "Aprovado no teste — toque pra mudar"
    : status === "reprovado" ? "Reprovado no teste — toque pra mudar"
    : status === "testando" ? "Testando — toque pra mudar"
    : "Marcar como testado";
  return (
    <>
      <button
        className={`${btnClass} ${isWishlist ? "active" : ""}`}
        onClick={(e) => { e.stopPropagation(); onToggleWishlist(productId); }}
        aria-label="Adicionar aos desejos"
        title="Desejos"
      >
        <Bookmark size={iconSize} strokeWidth={2} fill={isWishlist ? "currentColor" : "none"} />
      </button>
      <button
        className={`${btnClass} ${isNecessaire ? "active" : ""}`}
        onClick={(e) => { e.stopPropagation(); onToggleNecessaire(productId); }}
        aria-label={isNecessaire ? "Marcado na necessaire" : "Adicionar à necessaire"}
        title={isNecessaire ? "Marcado na necessaire" : "Necessaire"}
      >
        <NecessaireIcon size={iconSize} strokeWidth={2} fill={isNecessaire ? "currentColor" : "none"} />
      </button>
      <button
        className={`${btnClass} jota-status-btn ${status ? `status-${status}` : ""}`}
        onClick={(e) => { e.stopPropagation(); onCycleStatus(productId); }}
        aria-label={statusLabel}
        title={statusLabel}
      >
        {status === "aprovado" ? (
          <Check size={iconSize} strokeWidth={2.5} />
        ) : status === "reprovado" ? (
          <Ban size={iconSize} strokeWidth={2.25} />
        ) : (
          <FlaskConical size={iconSize} strokeWidth={2} fill={status === "testando" ? "currentColor" : "none"} />
        )}
      </button>
    </>
  );
}

function ProductCard({ product, onOpen, isFavorite, onToggleFavorite, tags }) {
  return (
    <button className="jota-pcard" onClick={() => onOpen(product)}>
      <div className="jota-pcard-photo">
        <ProductThumb name={product.name} photo={product.photo} size={144} />
      </div>
      <div className="jota-pcard-body">
        <span className="jota-pcard-name">{product.name}</span>
        <span className="jota-pcard-rating">
          <Star size={11} strokeWidth={0} fill="currentColor" />
          {product.rating.toFixed(1)}
          <span className="jota-pcard-rating-count">({product.ratingCount})</span>
        </span>
        <span className="jota-pcard-price">R$ {product.priceMin}–{product.priceMax}</span>
      </div>
    </button>
  );
}

function Accordion({ sections }) {
  const [openId, setOpenId] = useState(sections[0]?.id || null);
  return (
    <div className="jota-accordion">
      {sections.map((s) => (
        <div key={s.id} className="jota-accordion-item">
          <button className="jota-accordion-head" onClick={() => setOpenId(openId === s.id ? null : s.id)}>
            <span>{s.title}</span>
            <ChevronDown size={15} strokeWidth={2} className={`jota-accordion-chevron ${openId === s.id ? "open" : ""}`} />
          </button>
          {openId === s.id && <div className="jota-accordion-body">{s.content}</div>}
        </div>
      ))}
    </div>
  );
}

// Formata a data de uma avaliação: "hoje", "ontem" ou "11 jul".
function reviewDateLabel(iso) {
  if (!iso) return "";
  const d = new Date(iso);
  const now = new Date();
  const dayMs = 86400000;
  const startToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
  const startThat = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
  const diff = Math.round((startToday - startThat) / dayMs);
  if (diff <= 0) return "hoje";
  if (diff === 1) return "ontem";
  return d.toLocaleDateString("pt-BR", { day: "numeric", month: "short" });
}

// Timeline de avaliações da comunidade, com filtro por nº de estrelas.
function ReviewsTimeline({ reviews, loading }) {
  const [filter, setFilter] = useState(0); // 0 = todas
  const list = filter === 0 ? reviews : reviews.filter((r) => r.rating === filter);
  const withText = list.filter((r) => (r.comment || "").trim());

  return (
    <div className="jota-reviews">
      <div className="jota-reviews-filter">
        <button className={`jota-reviews-chip ${filter === 0 ? "active" : ""}`} onClick={() => setFilter(0)}>
          Todas
        </button>
        {[5, 4, 3, 2, 1].map((n) => (
          <button key={n} className={`jota-reviews-chip ${filter === n ? "active" : ""}`} onClick={() => setFilter(n)}>
            {n} <Star size={10} strokeWidth={0} fill="currentColor" />
          </button>
        ))}
      </div>

      {loading ? (
        <p className="jota-reviews-empty">Carregando avaliações…</p>
      ) : withText.length === 0 ? (
        <p className="jota-reviews-empty">
          {reviews.length === 0
            ? "Ainda não há avaliações escritas. Seja a primeira pessoa a avaliar 🤍"
            : "Nenhuma avaliação com esse número de estrelas ainda."}
        </p>
      ) : (
        <div className="jota-reviews-list">
          {withText.map((r) => {
            const name = r.anonymous ? "Anônimo" : (r.author_name || "Alguém");
            const initial = (name || "?").trim().charAt(0).toUpperCase();
            return (
              <div key={r.id} className="jota-review-item">
                <div className="jota-review-item-head">
                  <span className={`jota-review-avatar ${r.anonymous ? "anon" : ""}`}>{initial}</span>
                  <div className="jota-review-item-meta">
                    <span className="jota-review-item-name">{name}</span>
                    <span className="jota-review-item-stars">
                      {[1, 2, 3, 4, 5].map((n) => (
                        <Star key={n} size={11} strokeWidth={0} fill={n <= r.rating ? "currentColor" : "#DADADE"} />
                      ))}
                      <span className="jota-review-item-date">· {reviewDateLabel(r.created_at)}</span>
                    </span>
                  </div>
                </div>
                <p className="jota-review-item-text">{r.comment}</p>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function ProductDetailSheet({ product, onClose, myRating, onRate, onSubmitReview, isFavorite, onToggleFavorite, tags }) {
  const [comment, setComment] = useState("");
  const [anonymous, setAnonymous] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  const [brandNote, setBrandNote] = useState(false);
  const [reportOpen, setReportOpen] = useState(false);
  const [reportDraft, setReportDraft] = useState("");
  const [reportSent, setReportSent] = useState(false);
  const [shareOpen, setShareOpen] = useState(false);
  const [copied, setCopied] = useState(false);
  const [reviews, setReviews] = useState([]);
  const [reviewsLoading, setReviewsLoading] = useState(false);

  const loadReviews = async (pid) => {
    setReviewsLoading(true);
    const { data } = await supabase
      .from("product_reviews")
      .select("id, user_id, rating, comment, anonymous, author_name, created_at")
      .eq("product_id", pid)
      .order("created_at", { ascending: false });
    setReviews(data || []);
    setReviewsLoading(false);
  };

  useEffect(() => {
    setComment("");
    setAnonymous(false);
    setSubmitted(false);
    setBrandNote(false);
    setReportOpen(false);
    setReportDraft("");
    setReportSent(false);
    setShareOpen(false);
    setCopied(false);
    setReviews([]);
    if (product?.id) loadReviews(product.id);
  }, [product?.id]);

  const sendReport = () => {
    if (!reportDraft.trim()) return;
    setReportSent(true);
    setReportDraft("");
    setTimeout(() => { setReportSent(false); setReportOpen(false); }, 2200);
  };

  const submitReview = async () => {
    if (!myRating) return;
    if (onSubmitReview) await onSubmitReview(product.id, { rating: myRating, comment, anonymous });
    setSubmitted(true);
    if (product?.id) loadReviews(product.id);
  };

  // Link e ações de compartilhamento da página do produto.
  const shareUrl = product ? `https://jota.skin/?produto=${product.id}` : "";
  const shareText = product ? `Olha esse produto no J-OTA: ${product.name}` : "";
  const shareWhatsApp = () => {
    window.open(`https://wa.me/?text=${encodeURIComponent(shareText + " " + shareUrl)}`, "_blank", "noopener");
    setShareOpen(false);
  };
  const copyLink = async () => {
    try { await navigator.clipboard.writeText(shareUrl); }
    catch (e) { console.error("[J-OTA] copiar link falhou:", e); }
    setCopied(true);
    setTimeout(() => { setCopied(false); setShareOpen(false); }, 1600);
  };
  const nativeShare = () => {
    if (navigator.share) navigator.share({ title: product.name, text: shareText, url: shareUrl }).catch(() => {});
    setShareOpen(false);
  };

  // Ativos de uso profissional presentes neste produto (educativo/segurança).
  const proActivesInProduct = product
    ? [...RESTRICTED_ACTIVES].filter((a) => productHasActive(product, a))
    : [];
  // Retângulos de ativos: usa a lista tagueada; se vazia, cai nos ingredientes-chave.
  const activesList = product
    ? ((product.actives && product.actives.length ? product.actives : product.keyIngredients) || [])
    : [];

  return (
    <div className={`jota-product-sheet ${product ? "open" : ""}`}>
      {product && (
        <>
          <div className="jota-sheet-head">
            <span className="jota-sheet-head-icon"><Sparkle size={18} strokeWidth={1.9} /></span>
            <span className="jota-sheet-name">Produto</span>
            <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
              <X size={16} />
            </button>
          </div>
          <div className="jota-pdetail-body">
            <div className="jota-pdetail-actions">
              <button
                className={`jota-round-action ${isFavorite ? "active" : ""}`}
                onClick={onToggleFavorite}
                aria-label="Favoritar produto"
              >
                <Heart size={18} strokeWidth={2} fill={isFavorite ? "currentColor" : "none"} />
              </button>
              {tags && <ProductTagActions productId={product.id} {...tags} variant="row" />}
              <div className="jota-share-wrap">
                <button
                  className={`jota-round-action ${shareOpen ? "active" : ""}`}
                  onClick={() => setShareOpen((v) => !v)}
                  aria-label="Compartilhar produto"
                >
                  <Share2 size={17} strokeWidth={2} />
                </button>
                {shareOpen && (
                  <div className="jota-share-menu">
                    <button className="jota-share-opt" onClick={shareWhatsApp}>
                      <MessageCircle size={15} strokeWidth={2} /> WhatsApp
                    </button>
                    <button className="jota-share-opt" onClick={copyLink}>
                      <Copy size={15} strokeWidth={2} /> {copied ? "Link copiado!" : "Copiar link"}
                    </button>
                    {typeof navigator !== "undefined" && navigator.share && (
                      <button className="jota-share-opt" onClick={nativeShare}>
                        <Share2 size={15} strokeWidth={2} /> Mais opções
                      </button>
                    )}
                  </div>
                )}
              </div>
            </div>

            <div className="jota-pdetail-photo">
              <div className="jota-pdetail-photo-inner">
                <ProductThumb name={product.name} photo={product.photo} size={220} />
              </div>
            </div>

            <div className="jota-pdetail-name">{product.name}</div>
            <div className="jota-pdetail-rating">
              <Star size={13} strokeWidth={0} fill="currentColor" />
              {product.rating.toFixed(1)} · {product.ratingCount} avaliações
              <span className="jota-pdetail-rating-sep">·</span>
              <button className="jota-pdetail-brand-inline" onClick={() => setBrandNote((v) => !v)}>
                {product.brand}
              </button>
            </div>
            {brandNote && <div className="jota-brand-note">Página da marca em breve 🤍</div>}
            <div className="jota-pdetail-price">
              R$ {product.priceMin} – R$ {product.priceMax} · {product.size}{product.sizeUnit}
              <span className="jota-tag jota-tag-period jota-tag-inline">{PERIOD_LABEL[product.period]}</span>
            </div>

            {product.description && (
              <p className="jota-pdetail-desc">{product.description}</p>
            )}

            {proActivesInProduct.length > 0 && (
              <div className="jota-pro-note">
                <AlertCircle size={16} strokeWidth={1.9} />
                <div className="jota-pro-note-text">
                  <strong>{PRO_ACTIVE_LABEL}</strong>
                  <span>
                    Este produto contém {proActivesInProduct.join(", ")}. Recomendamos usar
                    com acompanhamento de um dermatologista ou profissional de saúde.
                  </span>
                </div>
              </div>
            )}

            <Accordion
              sections={[
                {
                  id: "concerns",
                  title: "Queixas",
                  content: product.concerns.length ? (
                    <div className="jota-tag-row">
                      {product.concerns.map((c) => <span key={c} className="jota-tag">{c}</span>)}
                    </div>
                  ) : (
                    <p className="jota-accordion-text">Sem queixas específicas cadastradas.</p>
                  ),
                },
                { id: "use", title: "Modo de uso", content: <p className="jota-accordion-text">{product.howToUse}</p> },
                {
                  id: "actives",
                  title: "Ativos",
                  content: activesList.length ? (
                    <div className="jota-tag-row">
                      {activesList.map((a) => <span key={a} className="jota-tag">{a}</span>)}
                    </div>
                  ) : (
                    <p className="jota-accordion-text">Ativos não informados.</p>
                  ),
                },
                { id: "contra", title: "Observações", content: <p className="jota-accordion-text">{product.contraindications}</p> },
                {
                  id: "reviews",
                  title: `Avaliações${reviews.length ? ` (${reviews.length})` : ""}`,
                  content: <ReviewsTimeline reviews={reviews} loading={reviewsLoading} />,
                },
              ]}
            />

            <div className="jota-pdetail-myrating">
              <span className="jota-pdetail-myrating-label">Sua avaliação</span>
              <div className="jota-star-picker">
                {[1, 2, 3, 4, 5].map((n) => (
                  <button key={n} onClick={() => onRate(product.id, n)} aria-label={`Avaliar com ${n} estrelas`}>
                    <Star size={22} strokeWidth={1.5} fill={n <= (myRating || 0) ? "currentColor" : "none"} />
                  </button>
                ))}
              </div>

              {myRating > 0 && !submitted && (
                <div className="jota-review-form">
                  <textarea
                    className="jota-review-textarea"
                    placeholder="Conte sua experiência com esse produto (opcional)"
                    value={comment}
                    onChange={(e) => setComment(e.target.value)}
                    rows={3}
                  />
                  <label className="jota-review-anon">
                    <input
                      type="checkbox"
                      checked={anonymous}
                      onChange={(e) => setAnonymous(e.target.checked)}
                    />
                    <span>Publicar como anônimo</span>
                  </label>
                  <button className="jota-review-submit" onClick={submitReview}>
                    Publicar avaliação
                  </button>
                </div>
              )}

              {submitted && (
                <div className="jota-review-sent">Agradecemos sua avaliação!</div>
              )}
            </div>

            <a className="jota-pdetail-buy" href={product.buyLink} target="_blank" rel="noreferrer">
              Ver oferta com desconto <ExternalLink size={15} strokeWidth={2} />
            </a>

            <div className="jota-report-footer">
              {reportSent ? (
                <div className="jota-suggest-sent">Reporte enviado, vamos revisar! 🤍</div>
              ) : !reportOpen ? (
                <button className="jota-report-link" onClick={() => setReportOpen(true)}>
                  <Flag size={11} strokeWidth={2} /> Reportar erro nesse produto
                </button>
              ) : (
                <div className="jota-suggest-form">
                  <input
                    className="jota-add-input jota-suggest-input"
                    placeholder="O que está errado?"
                    value={reportDraft}
                    autoFocus
                    onChange={(e) => setReportDraft(e.target.value)}
                    onKeyDown={(e) => { if (e.key === "Enter") sendReport(); }}
                  />
                  <button className="jota-add-confirm" onClick={sendReport} aria-label="Enviar reporte">
                    <Check size={13} strokeWidth={2} />
                  </button>
                </div>
              )}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function FeaturedProductBanner({ onOpen }) {
  // Recalculado a cada render (não como constante de módulo) pra refletir o
  // catálogo real vindo do banco, que só chega depois do loadCatalog() no login.
  const topRated = [...PRODUCT_CATALOG].sort((a, b) => b.rating - a.rating).slice(0, 3);
  const [idx, setIdx] = useState(0);

  useEffect(() => {
    if (!topRated.length) return;
    const id = setInterval(() => setIdx((i) => (i + 1) % topRated.length), 4500);
    return () => clearInterval(id);
  }, [topRated.length]);

  if (!topRated.length) return null;
  const p = topRated[idx % topRated.length];

  return (
    <button className="jota-featured-banner" onClick={() => onOpen(p)}>
      <ProductThumb name={p.name} photo={p.photo} size={52} />
      <div className="jota-featured-text">
        <span className="jota-promo-label">Em destaque</span>
        <span className="jota-featured-title">{p.name}</span>
        <span className="jota-promo-sub">
          <Star size={11} strokeWidth={0} fill="currentColor" /> {p.rating.toFixed(1)} · R$ {p.priceMin}–{p.priceMax}
        </span>
      </div>
      <div className="jota-featured-dots">
        {topRated.map((_, i) => (
          <span key={i} className={`jota-featured-dot ${i === idx ? "active" : ""}`} />
        ))}
      </div>
    </button>
  );
}

// Botão da fileira de coleções (Favoritos/Desejos/Necessaire/Testados na Busca;
// Necessaire/Testados/Resultados/Histórico no Diário). Reaproveitado nas duas telas.
function CollectionChip({ icon, label, count, sub, onClick, labelWrap = false }) {
  return (
    <button className="jota-collection-chip" onClick={onClick}>
      <span className="jota-collection-chip-icon">{icon}</span>
      <span className="jota-collection-chip-text">
        <span className={`jota-collection-chip-label ${labelWrap ? "wrap" : ""}`}>{label}</span>
        {sub ? (
          <span className="jota-collection-chip-count">{sub}</span>
        ) : typeof count === "number" ? (
          <span className="jota-collection-chip-count">{count} {count === 1 ? "item" : "itens"}</span>
        ) : null}
      </span>
    </button>
  );
}


// Chave local pra lembrar que a pessoa já disse "estou ciente" no aviso de
// sequência de 5 estrelas — assim ele para de aparecer pra sempre nesse aparelho.
const FIVESTAR_DISMISS_KEY = "jota-fivestar-warning-dismissed";

function RateProductsSheet({ open, onClose, myRatings, onRate, onEarnPoints, favorites, onToggleFavorite, onViewProduct, routines, tagsFor }) {
  const scrollRef = useRef(null);
  const [activeIdx, setActiveIdx] = useState(0);
  const [toast, setToast] = useState(null);
  const [orderedIds, setOrderedIds] = useState(() => PRODUCT_CATALOG.map((p) => p.id));
  const [postponedIds, setPostponedIds] = useState(() => new Set());
  const [fiveStreak, setFiveStreak] = useState(0);
  const [pendingFive, setPendingFive] = useState(null);
  const [streakWarningDismissed, setStreakWarningDismissed] = useState(
    () => localStorage.getItem(FIVESTAR_DISMISS_KEY) === "1"
  );
  const [dismissChecked, setDismissChecked] = useState(false);
  const activeIdxRef = useRef(0);
  const scrollTimeout = useRef(null);

  const usedProductIds = new Set(
    (routines || []).flatMap((r) => r.steps.map((s) => s.productId).filter(Boolean))
  );

  const ordered = orderedIds.map((id) => PRODUCT_CATALOG.find((p) => p.id === id)).filter(Boolean);
  const allResolved = PRODUCT_CATALOG.every((p) => !!myRatings[p.id] || postponedIds.has(p.id));
  const slideCount = ordered.length + (allResolved ? 1 : 0);

  useEffect(() => {
    if (open) {
      const sorted = [...PRODUCT_CATALOG].sort((a, b) => {
        const resolvedA = !!myRatings[a.id] || postponedIds.has(a.id);
        const resolvedB = !!myRatings[b.id] || postponedIds.has(b.id);
        if (resolvedA !== resolvedB) return resolvedA ? 1 : -1;
        const usedA = usedProductIds.has(a.id);
        const usedB = usedProductIds.has(b.id);
        if (usedA !== usedB) return usedA ? -1 : 1;
        return 0;
      });
      setOrderedIds(sorted.map((p) => p.id));
      if (scrollRef.current) scrollRef.current.scrollTop = 0;
      setActiveIdx(0);
      activeIdxRef.current = 0;
      setFiveStreak(0);
      setPendingFive(null);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  const goToIndex = (idx) => {
    const el = scrollRef.current;
    if (!el || !el.clientHeight) return;
    const maxIdx = Math.round(el.scrollHeight / el.clientHeight) - 1;
    const clamped = Math.min(Math.max(idx, 0), maxIdx);
    el.scrollTo({ top: clamped * el.clientHeight, behavior: "smooth" });
  };

  const commitRating = (product, n) => {
    const alreadyRated = !!myRatings[product.id];
    onRate(product.id, n);
    if (!alreadyRated) {
      onEarnPoints(1, `Avaliação: ${product.name}`);
      setToast("+1 Atom!");
      setTimeout(() => setToast(null), 1400);
    }
    setTimeout(() => goToIndex(activeIdxRef.current + 1), 550);
  };

  const handleRate = (product, n) => {
    if (n === 5 && !streakWarningDismissed) {
      const nextStreak = fiveStreak + 1;
      if (nextStreak >= 4) {
        setPendingFive({ product, n });
        return;
      }
      setFiveStreak(nextStreak);
    } else {
      setFiveStreak(0);
    }
    commitRating(product, n);
  };

  const confirmPendingFive = () => {
    if (!pendingFive) return;
    if (dismissChecked) {
      localStorage.setItem(FIVESTAR_DISMISS_KEY, "1");
      setStreakWarningDismissed(true);
    }
    setFiveStreak(0);
    commitRating(pendingFive.product, pendingFive.n);
    setPendingFive(null);
    setDismissChecked(false);
  };

  const reconsiderPendingFive = () => {
    setPendingFive(null);
    setDismissChecked(false);
  };

  const handlePostpone = (product) => {
    setPostponedIds((prev) => {
      const next = new Set(prev);
      next.add(product.id);
      return next;
    });
    setFiveStreak(0);
    setTimeout(() => goToIndex(activeIdxRef.current + 1), 250);
  };

  const handleScroll = () => {
    const el = scrollRef.current;
    if (!el || !el.clientHeight) return;
    if (scrollTimeout.current) clearTimeout(scrollTimeout.current);
    scrollTimeout.current = setTimeout(() => {
      const idx = Math.round(el.scrollTop / el.clientHeight);
      activeIdxRef.current = idx;
      setActiveIdx(idx);
    }, 100);
  };

  return (
    <div className={`jota-rate-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><Star size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">Avalie e ganhe Atons</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>

      <div className="jota-rate-progress">
        {Array.from({ length: slideCount }).map((_, i) => (
          <span key={i} className={`jota-rate-progress-dot ${i === activeIdx ? "active" : ""} ${i < activeIdx ? "passed" : ""}`} />
        ))}
      </div>

      <div className="jota-rate-scroll" ref={scrollRef} onScroll={handleScroll}>
        {ordered.map((p, idx) => {
          const rating = myRatings[p.id] || 0;
          const isFav = !!favorites?.[p.id];
          const isPostponed = postponedIds.has(p.id);
          const isUsed = usedProductIds.has(p.id);
          return (
            <div key={p.id} className={`jota-rate-slide ${isUsed ? "has-used-badge" : ""}`}>
              <div className="jota-rate-corner-actions">
                <button
                  className={`jota-round-action ${isFav ? "active" : ""}`}
                  onClick={() => onToggleFavorite(p.id)}
                  aria-label="Favoritar produto"
                >
                  <Heart size={16} strokeWidth={2} fill={isFav ? "currentColor" : "none"} />
                </button>
                {tagsFor && <ProductTagActions productId={p.id} {...tagsFor(p.id)} variant="row" />}
                <button className="jota-round-action" onClick={() => onViewProduct(p)} aria-label="Ver produto">
                  <Search size={15} strokeWidth={2} />
                </button>
              </div>

              {isUsed && <span className="jota-rate-used-badge">Você usa esse</span>}

              <div className="jota-rate-photo">
                <ProductThumb name={p.name} photo={p.photo} size={156} />
              </div>

              <span className="jota-rate-name">{p.name}</span>
              <span className="jota-rate-meta">
                <Star size={12} strokeWidth={0} fill="currentColor" /> {p.rating.toFixed(1)} · {p.ratingCount} avaliações · {p.brand}
              </span>
              <span className="jota-rate-price">R$ {p.priceMin} – R$ {p.priceMax} · {p.size}{p.sizeUnit}</span>
              <p className="jota-rate-desc">{p.description}</p>

              {rating === 0 && !isPostponed && (
                <span className="jota-rate-choice-label">Avaliar agora</span>
              )}

              <div className="jota-rate-stars">
                {[1, 2, 3, 4, 5].map((n) => (
                  <button key={n} onClick={() => handleRate(p, n)} aria-label={`Avaliar com ${n} estrelas`}>
                    <Star size={30} strokeWidth={1.3} fill={n <= rating ? "currentColor" : "none"} />
                  </button>
                ))}
              </div>

              {(rating > 0 || isPostponed) && (
                <span className="jota-rate-hint">
                  {isPostponed ? "Deixado pra depois — deslize pra continuar" : "Avaliado — deslize pra continuar"}
                </span>
              )}

              {rating === 0 && !isPostponed && (
                <button className="jota-rate-later-btn" onClick={() => handlePostpone(p)}>
                  Avaliar depois
                </button>
              )}

              {idx === 0 && (
                <div className="jota-rate-scroll-hint" aria-hidden="true">
                  <span>Role para ver mais produtos</span>
                  <ChevronDown size={16} strokeWidth={2} />
                </div>
              )}
            </div>
          );
        })}

        {allResolved && (
          <div className="jota-rate-slide jota-rate-done-slide">
            <span className="jota-rate-done-badge">
              <Check size={28} strokeWidth={2.5} />
            </span>
            <span className="jota-rate-name">Você concluiu tudo por hoje!</span>
            <p className="jota-rate-desc">Volte amanhã pra avaliar mais produtos e ganhar mais Atons.</p>
            <button className="jota-review-submit jota-rate-done-close" onClick={onClose}>
              Fechar
            </button>
          </div>
        )}
      </div>

      {toast && <div className="jota-rate-toast">{toast}</div>}

      {pendingFive && (
        <div className="jota-honesty-overlay">
          <div className="jota-honesty-card">
            <span className="jota-honesty-title">Só confirmando...</span>
            <p className="jota-honesty-text">
              Você já deu nota máxima pra vários produtos seguidos. Avalie com calma e sinceridade —
              isso ajuda outras pessoas a escolherem melhor.
            </p>
            <label className="jota-review-anon">
              <input
                type="checkbox"
                checked={dismissChecked}
                onChange={(e) => setDismissChecked(e.target.checked)}
              />
              <span>Estou ciente, não me lembrar novamente</span>
            </label>
            <button className="jota-review-submit" onClick={confirmPendingFive}>
              Confirmo, é 5 estrelas mesmo
            </button>
            <button className="jota-honesty-secondary" onClick={reconsiderPendingFive}>
              Quero reconsiderar
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

// Banner colorido (com efeito glass) reaproveitado no topo das coleções —
// Necessaire, Favoritos, Desejos, Testados. Só muda gradiente/halo/ícone/texto.
function CollectionCountBanner({ gradient, haloColor, icon, cap, count, unit = ["produto", "produtos"] }) {
  return (
    <div className="jota-diario-banner" style={{ background: gradient }}>
      <div className="jota-diario-banner-haze" />
      <span className="jota-diario-banner-seed">
        <span className="jota-diario-banner-halo" style={{ background: `radial-gradient(circle, ${haloColor}, transparent 66%)`, opacity: 0.45 }} />
        {icon}
      </span>
      <div className="jota-diario-banner-info">
        <span className="jota-diario-banner-cap">{cap}</span>
        <span className="jota-diario-banner-lvl">{count} {count === 1 ? unit[0] : unit[1]}</span>
      </div>
    </div>
  );
}

// Sheet cheia pra uma coleção de produtos (Favoritos e Desejos) — mesmo template
// da Necessaire: banner colorido, "Adicionar produto" e organização por
// compartimento (etapa). Genérica via props: membership (mapa id→bool) + onToggle.
function ProductCollectionSheet({
  open, onClose, title, headIcon, banner, membership, onToggle, productStatus = {},
  onOpenProduct, addLabel = "Adicionar produto", emptyTitle, emptySub,
}) {
  const [adding, setAdding] = useState(false);
  const [query, setQuery] = useState("");
  useEffect(() => { if (!open) { setAdding(false); setQuery(""); } }, [open]);

  const products = Object.keys(membership || {})
    .filter((id) => membership[id])
    .map((id) => PRODUCT_CATALOG.find((p) => p.id === id))
    .filter(Boolean);

  const compartments = STEP_TYPES
    .map((type) => ({ type, items: products.filter((p) => p.stepType === type) }))
    .filter((c) => c.items.length > 0);
  const others = products.filter((p) => !STEP_TYPES.includes(p.stepType));
  if (others.length) compartments.push({ type: "Outros", items: others });

  const q = query.trim().toLowerCase();
  const addable = PRODUCT_CATALOG
    .filter((p) => !membership[p.id])
    .filter((p) => !q || p.name.toLowerCase().includes(q) || (p.brand || "").toLowerCase().includes(q));

  return (
    <div className={`jota-product-sheet ${open ? "open" : ""}`}>
      {open && (
        <>
          <div className="jota-sheet-head">
            <span className="jota-sheet-head-icon">{headIcon}</span>
            <span className="jota-sheet-name">{title}</span>
            <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
              <X size={16} />
            </button>
          </div>
          <div className="jota-pdetail-body">
            <CollectionCountBanner {...banner} count={products.length} />

            {adding ? (
              <div className="jota-product-picker">
                <input
                  className="jota-product-search"
                  placeholder="Pesquise um produto pra adicionar"
                  value={query}
                  autoFocus
                  onChange={(e) => setQuery(e.target.value)}
                />
                <div className="jota-product-list">
                  {addable.length === 0 ? (
                    <div className="jota-necessaire-add-empty">Nenhum produto pra adicionar.</div>
                  ) : addable.map((p) => (
                    <button key={p.id} className="jota-product-option" onClick={() => onToggle(p.id)}>
                      <ProductThumb name={p.name} photo={p.photo} size={28} />
                      <span className="jota-product-option-text">
                        <span className="jota-product-option-name">{p.name}</span>
                        <span className="jota-product-option-brand">{p.brand}</span>
                      </span>
                      <span className="jota-necessaire-add-plus"><Plus size={14} strokeWidth={2.5} /></span>
                    </button>
                  ))}
                </div>
                <button className="jota-add-cancel-btn" onClick={() => { setAdding(false); setQuery(""); }}>Concluir</button>
              </div>
            ) : (
              <button className="jota-necessaire-add-btn" onClick={() => setAdding(true)}>
                <Plus size={15} strokeWidth={2} /> {addLabel}
              </button>
            )}

            {products.length === 0 ? (
              <div className="jota-empty">
                <div className="jota-empty-title">{emptyTitle}</div>
                <div className="jota-empty-sub">{emptySub}</div>
              </div>
            ) : (
              compartments.map((c) => (
                <div key={c.type} className="jota-necessaire-compartment">
                  <div className="jota-necessaire-compartment-head">
                    <span>{c.type}</span>
                    <span className="jota-necessaire-compartment-count">{c.items.length}</span>
                  </div>
                  <div className="jota-necessaire-row">
                    {c.items.map((p) => (
                      <button key={p.id} className="jota-necessaire-item" onClick={() => onOpenProduct(p)}>
                        <span className="jota-necessaire-item-remove" onClick={(e) => { e.stopPropagation(); onToggle(p.id); }} aria-label="Remover">
                          <X size={12} strokeWidth={2.5} />
                        </span>
                        <ProductThumb name={p.name} photo={p.photo} size={64} />
                        <span className="jota-necessaire-item-name">{p.name}</span>
                        <TestStatusBadge status={productStatus[p.id]} />
                      </button>
                    ))}
                  </div>
                </div>
              ))
            )}
          </div>
        </>
      )}
    </div>
  );
}

// Selo de status de teste (bolinha colorida + texto) usado nas listagens compactas.
function TestStatusBadge({ status }) {
  if (!status) return null;
  const label = status === "aprovado" ? "Aprovado" : status === "reprovado" ? "Reprovado" : "Testando";
  return <span className={`jota-status-badge status-${status}`}>{label}</span>;
}

// Nécessaire: organizada por compartimento (a mesma taxonomia de "Etapa" já
// usada nos filtros da Busca — STEP_TYPES), como as gavetas/bolsos de um
// nécessaire de verdade. Produto sem etapa reconhecida cai em "Outros".
function NecessaireSheet({ open, onClose, necessaire, productStatus, routines = [], onOpenProduct, onToggleNecessaire }) {
  const [adding, setAdding] = useState(false);
  const [query, setQuery] = useState("");
  useEffect(() => { if (!open) { setAdding(false); setQuery(""); } }, [open]);

  const products = Object.keys(necessaire || {})
    .filter((id) => necessaire[id])
    .map((id) => PRODUCT_CATALOG.find((p) => p.id === id))
    .filter(Boolean);

  const compartments = STEP_TYPES
    .map((type) => ({ type, items: products.filter((p) => p.stepType === type) }))
    .filter((c) => c.items.length > 0);
  const others = products.filter((p) => !STEP_TYPES.includes(p.stepType));
  if (others.length) compartments.push({ type: "Outros", items: others });

  // Produtos que estão em alguma rotina, mas foram tirados da necessaire.
  const routineProductIds = [...new Set((routines || []).flatMap((r) => (r.steps || []).map((s) => s.productId).filter(Boolean)))];
  const removedRoutineProducts = routineProductIds
    .filter((id) => !necessaire[id])
    .map((id) => PRODUCT_CATALOG.find((p) => p.id === id))
    .filter(Boolean);

  // Catálogo disponível pra adicionar (o que ainda não está na necessaire), filtrado pela busca.
  const q = query.trim().toLowerCase();
  const addable = PRODUCT_CATALOG
    .filter((p) => !necessaire[p.id])
    .filter((p) => !q || p.name.toLowerCase().includes(q) || (p.brand || "").toLowerCase().includes(q));

  return (
    <div className={`jota-product-sheet ${open ? "open" : ""}`}>
      {open && (
        <>
          <div className="jota-sheet-head">
            <span className="jota-sheet-head-icon"><NecessaireIcon size={19} strokeWidth={1.9} /></span>
            <span className="jota-sheet-name">Necessaire</span>
            <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
              <X size={16} />
            </button>
          </div>
          <div className="jota-pdetail-body">
            <NecessaireCountBanner count={products.length} />

            {adding ? (
              <div className="jota-product-picker">
                <input
                  className="jota-product-search"
                  placeholder="Pesquise um produto pra adicionar"
                  value={query}
                  autoFocus
                  onChange={(e) => setQuery(e.target.value)}
                />
                <div className="jota-product-list">
                  {addable.length === 0 ? (
                    <div className="jota-necessaire-add-empty">Nenhum produto pra adicionar.</div>
                  ) : addable.map((p) => (
                    <button key={p.id} className="jota-product-option" onClick={() => onToggleNecessaire(p.id)}>
                      <ProductThumb name={p.name} photo={p.photo} size={28} />
                      <span className="jota-product-option-text">
                        <span className="jota-product-option-name">{p.name}</span>
                        <span className="jota-product-option-brand">{p.brand}</span>
                      </span>
                      <span className="jota-necessaire-add-plus"><Plus size={14} strokeWidth={2.5} /></span>
                    </button>
                  ))}
                </div>
                <button className="jota-add-cancel-btn" onClick={() => { setAdding(false); setQuery(""); }}>Concluir</button>
              </div>
            ) : (
              <button className="jota-necessaire-add-btn" onClick={() => setAdding(true)}>
                <Plus size={15} strokeWidth={2} /> Adicionar produto
              </button>
            )}

            {products.length === 0 ? (
              <div className="jota-empty">
                <div className="jota-empty-title">Nada aqui ainda</div>
                <div className="jota-empty-sub">
                  Toque em "Adicionar produto", marque produtos com o ícone de bolsinha, ou adicione
                  um produto oficial numa rotina que ele entra aqui sozinho.
                </div>
              </div>
            ) : (
              compartments.map((c) => (
                <div key={c.type} className="jota-necessaire-compartment">
                  <div className="jota-necessaire-compartment-head">
                    <span>{c.type}</span>
                    <span className="jota-necessaire-compartment-count">{c.items.length}</span>
                  </div>
                  <div className="jota-necessaire-row">
                    {c.items.map((p) => (
                      <button key={p.id} className="jota-necessaire-item" onClick={() => onOpenProduct(p)}>
                        <span className="jota-necessaire-item-remove" onClick={(e) => { e.stopPropagation(); onToggleNecessaire(p.id); }} aria-label="Remover da necessaire">
                          <X size={12} strokeWidth={2.5} />
                        </span>
                        <ProductThumb name={p.name} photo={p.photo} size={64} />
                        <span className="jota-necessaire-item-name">{p.name}</span>
                        <TestStatusBadge status={productStatus[p.id]} />
                      </button>
                    ))}
                  </div>
                </div>
              ))
            )}

            {removedRoutineProducts.length > 0 && (
              <div className="jota-necessaire-removed">
                <div className="jota-necessaire-removed-title">
                  Produtos da sua rotina que não estão mais na sua necessaire
                </div>
                <div className="jota-necessaire-row">
                  {removedRoutineProducts.map((p) => (
                    <button
                      key={p.id}
                      className="jota-necessaire-item jota-necessaire-item-ghost"
                      onClick={() => onToggleNecessaire(p.id)}
                      aria-label="Adicionar de volta à necessaire"
                    >
                      <span className="jota-necessaire-item-add"><Plus size={14} strokeWidth={3} /></span>
                      <ProductThumb name={p.name} photo={p.photo} size={64} />
                      <span className="jota-necessaire-item-name">{p.name}</span>
                    </button>
                  ))}
                </div>
              </div>
            )}
          </div>
        </>
      )}
    </div>
  );
}


// Testados: no topo, os produtos da necessaire ainda sem avaliação (nome + 3
// botões redondos sem cor). Ao escolher, o produto cai num bloco por status
// embaixo (estilo necessaire).
function TestadosSheet({ open, onClose, productStatus, necessaire = {}, onOpenProduct, onSetStatus }) {
  const [adding, setAdding] = useState(false);
  const [query, setQuery] = useState("");
  useEffect(() => { if (!open) { setAdding(false); setQuery(""); } }, [open]);

  const CHOICES = [
    { key: "aprovado", label: "Aprovado", icon: <Check size={13} strokeWidth={2.5} /> },
    { key: "testando", label: "Testando", icon: <FlaskConical size={13} strokeWidth={2} /> },
    { key: "reprovado", label: "Reprovado", icon: <Ban size={13} strokeWidth={2.25} /> },
  ];
  const sections = [
    { key: "aprovado", label: "Aprovados" },
    { key: "testando", label: "Testando" },
    { key: "reprovado", label: "Reprovados" },
  ];

  const untested = Object.keys(necessaire)
    .filter((id) => necessaire[id] && !productStatus[id])
    .map((id) => PRODUCT_CATALOG.find((p) => p.id === id))
    .filter(Boolean);
  const total = Object.keys(productStatus || {}).length;

  // Catálogo pra adicionar direto pra testar (o que ainda não tem avaliação).
  const q = query.trim().toLowerCase();
  const addable = PRODUCT_CATALOG
    .filter((p) => !productStatus[p.id])
    .filter((p) => !q || p.name.toLowerCase().includes(q) || (p.brand || "").toLowerCase().includes(q));

  return (
    <div className={`jota-product-sheet ${open ? "open" : ""}`}>
      {open && (
        <>
          <div className="jota-sheet-head">
            <span className="jota-sheet-head-icon"><FlaskConical size={18} strokeWidth={1.9} /></span>
            <span className="jota-sheet-name">Testados</span>
            <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
              <X size={16} />
            </button>
          </div>
          <div className="jota-pdetail-body">
            <CollectionCountBanner
              gradient="linear-gradient(110deg, #2BB6A3 0%, #3D9BE0 55%, #6D6FE0 100%)"
              haloColor="rgba(61,155,224,0.6)"
              icon={<FlaskConical size={24} strokeWidth={1.75} />}
              cap="Seus testados"
              count={total}
              unit={["produto avaliado", "produtos avaliados"]}
            />

            {adding ? (
              <div className="jota-product-picker">
                <input
                  className="jota-product-search"
                  placeholder="Pesquise um produto pra testar"
                  value={query}
                  autoFocus
                  onChange={(e) => setQuery(e.target.value)}
                />
                <div className="jota-product-list">
                  {addable.length === 0 ? (
                    <div className="jota-necessaire-add-empty">Nenhum produto pra adicionar.</div>
                  ) : addable.map((p) => (
                    <button key={p.id} className="jota-product-option" onClick={() => onSetStatus(p.id, "testando")}>
                      <ProductThumb name={p.name} photo={p.photo} size={28} />
                      <span className="jota-product-option-text">
                        <span className="jota-product-option-name">{p.name}</span>
                        <span className="jota-product-option-brand">{p.brand}</span>
                      </span>
                      <span className="jota-necessaire-add-plus"><Plus size={14} strokeWidth={2.5} /></span>
                    </button>
                  ))}
                </div>
                <button className="jota-add-cancel-btn" onClick={() => { setAdding(false); setQuery(""); }}>Concluir</button>
              </div>
            ) : (
              <button className="jota-necessaire-add-btn" onClick={() => setAdding(true)}>
                <Plus size={15} strokeWidth={2} /> Adicionar produto
              </button>
            )}

            {untested.length > 0 && (
              <div className="jota-testados-eval">
                {untested.map((p) => (
                  <div key={p.id} className="jota-testados-eval-row">
                    <button className="jota-testados-eval-info" onClick={() => onOpenProduct(p)}>
                      <ProductThumb name={p.name} photo={p.photo} size={40} />
                      <span className="jota-testados-eval-name">{p.name}</span>
                    </button>
                    <div className="jota-testados-choices">
                      {CHOICES.map((c) => (
                        <button key={c.key} className={`jota-testados-choice status-${c.key}`} onClick={() => onSetStatus(p.id, c.key)}>
                          {c.icon}{c.label}
                        </button>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            )}

            {total === 0 && untested.length === 0 ? (
              <div className="jota-empty">
                <div className="jota-empty-title">Nada testado ainda</div>
                <div className="jota-empty-sub">
                  Adicione produtos na sua necessaire pra avaliar aqui, ou toque no ícone de frasco
                  (<FlaskConical size={12} strokeWidth={2} style={{ verticalAlign: "middle" }} />) na página do produto.
                </div>
              </div>
            ) : (
              sections.map((s) => {
                const items = Object.keys(productStatus)
                  .filter((id) => productStatus[id] === s.key)
                  .map((id) => PRODUCT_CATALOG.find((p) => p.id === id))
                  .filter(Boolean);
                if (!items.length) return null;
                return (
                  <div key={s.key} className="jota-necessaire-compartment">
                    <div className="jota-necessaire-compartment-head">
                      <span>{s.label}</span>
                      <span className="jota-necessaire-compartment-count">{items.length}</span>
                    </div>
                    <div className="jota-necessaire-row">
                      {items.map((p) => (
                        <button key={p.id} className="jota-necessaire-item" onClick={() => onOpenProduct(p)}>
                          <span className="jota-necessaire-item-remove" onClick={(e) => { e.stopPropagation(); onSetStatus(p.id, null); }} aria-label="Tirar avaliação">
                            <X size={12} strokeWidth={2.5} />
                          </span>
                          <ProductThumb name={p.name} photo={p.photo} size={64} />
                          <span className="jota-necessaire-item-name">{p.name}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                );
              })
            )}
          </div>
        </>
      )}
    </div>
  );
}

// Grupo de filtro em formato "menu suspenso" (acordeão). Fica fechado por
// padrão pra não ocupar a tela toda; o contador mostra quantos itens do grupo
// estão ativos mesmo quando fechado.
function FilterGroup({ label, hint, count = 0, children }) {
  const [open, setOpen] = useState(false);
  return (
    <div className={`jota-filters-group ${open ? "open" : ""}`}>
      <button className="jota-filters-group-head" onClick={() => setOpen((o) => !o)}>
        <span className="jota-filters-label">
          {label}
          {hint && <span className="jota-filters-hint"> {hint}</span>}
        </span>
        <span className="jota-filters-group-meta">
          {count > 0 && <span className="jota-filters-count">{count}</span>}
          <ChevronDown size={16} strokeWidth={2} className="jota-filters-chevron" />
        </span>
      </button>
      {open && <div className="jota-filters-group-body">{children}</div>}
    </div>
  );
}

function BuscaScreen({
  onEarnPoints, routines, myRatings, onRate, onSubmitReview, favorites, toggleFavorite,
  wishlist, toggleWishlist, necessaire, productStatus, tagsFor, onOpenNecessaire, onOpenTestados,
}) {
  const [query, setQuery] = useState("");
  const [concernFilters, setConcernFilters] = useState([]);
  const [periodFilter, setPeriodFilter] = useState("todos");
  const [priceRange, setPriceRange] = useState([PRICE_MIN, PRICE_MAX]);
  const [sizeFilter, setSizeFilter] = useState(null);
  const [stepTypeFilter, setStepTypeFilter] = useState(null);
  const [bodyAreaFilter, setBodyAreaFilter] = useState(null);
  const [brandFilter, setBrandFilter] = useState(null);
  const [ativoFilters, setAtivoFilters] = useState([]);
  const [ativoMatchMode, setAtivoMatchMode] = useState("algum"); // "algum" (pelo menos um) | "todos"
  const [rateSheetOpen, setRateSheetOpen] = useState(false);
  const [favSheetOpen, setFavSheetOpen] = useState(false);
  const [wishSheetOpen, setWishSheetOpen] = useState(false);
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [selectedProduct, setSelectedProduct] = useState(null);
  const [suggestOpen, setSuggestOpen] = useState(false);
  const [suggestDraft, setSuggestDraft] = useState("");
  const [suggestSent, setSuggestSent] = useState(false);

  const toggleConcern = (c) => {
    setConcernFilters((f) => (f.includes(c) ? f.filter((x) => x !== c) : [...f, c]));
  };

  const toggleAtivo = (a) => {
    setAtivoFilters((f) => (f.includes(a) ? f.filter((x) => x !== a) : [...f, a]));
  };

  const brands = [...new Set(PRODUCT_CATALOG.map((p) => p.brand))];

  const clearFilters = () => {
    setConcernFilters([]);
    setPeriodFilter("todos");
    setPriceRange([PRICE_MIN, PRICE_MAX]);
    setSizeFilter(null);
    setStepTypeFilter(null);
    setBodyAreaFilter(null);
    setBrandFilter(null);
    setAtivoFilters([]);
    setFiltersOpen(false);
  };

  const priceActive = priceRange[0] > PRICE_MIN || priceRange[1] < PRICE_MAX;
  const otherFilterCount = (periodFilter !== "todos" ? 1 : 0) + (priceActive ? 1 : 0) + (sizeFilter ? 1 : 0)
    + (stepTypeFilter ? 1 : 0) + (bodyAreaFilter ? 1 : 0) + (brandFilter ? 1 : 0) + ativoFilters.length;

  const results = PRODUCT_CATALOG.filter((p) => {
    const q = query.trim().toLowerCase();
    const matchesQuery = !q
      || p.name.toLowerCase().includes(q)
      || p.brand.toLowerCase().includes(q)
      || p.description.toLowerCase().includes(q)
      || p.concerns.some((c) => c.toLowerCase().includes(q));
    const matchesConcerns = concernFilters.length === 0 || concernFilters.every((c) => p.concerns.includes(c));
    const matchesPeriod = periodFilter === "todos" || p.period === periodFilter || p.period === "ambos";
    const matchesPrice = p.priceMax >= priceRange[0] && p.priceMin <= priceRange[1];
    const matchesSize = !sizeFilter || SIZE_BRACKETS.find((b) => b.id === sizeFilter)?.test(p);
    const matchesStepType = !stepTypeFilter || p.stepType === stepTypeFilter;
    const matchesBodyArea = !bodyAreaFilter || p.bodyArea === bodyAreaFilter;
    const matchesBrand = !brandFilter || p.brand === brandFilter;
    // Ativos: "todos" exige todos os selecionados; "algum" exige pelo menos um.
    const matchesAtivos = ativoFilters.length === 0 || (
      ativoMatchMode === "todos"
        ? ativoFilters.every((a) => productHasActive(p, a))
        : ativoFilters.some((a) => productHasActive(p, a))
    );
    return matchesQuery && matchesConcerns && matchesPeriod && matchesPrice && matchesSize && matchesStepType && matchesBodyArea && matchesBrand && matchesAtivos;
  });

  const sendSuggestion = () => {
    if (!suggestDraft.trim()) return;
    setSuggestSent(true);
    setSuggestDraft("");
    setTimeout(() => { setSuggestSent(false); setSuggestOpen(false); }, 2200);
  };

  return (
    <>
      <FeaturedProductBanner onOpen={setSelectedProduct} />

      <div className="jota-collection-row">
        <CollectionChip
          icon={<Heart size={23} strokeWidth={1.75} />}
          label="Favoritos"
          count={Object.values(favorites).filter(Boolean).length}
          onClick={() => setFavSheetOpen(true)}
        />
        <CollectionChip
          icon={<Bookmark size={23} strokeWidth={1.75} />}
          label="Desejos"
          count={Object.values(wishlist).filter(Boolean).length}
          onClick={() => setWishSheetOpen(true)}
        />
        <CollectionChip
          icon={<NecessaireIcon size={23} strokeWidth={1.75} />}
          label="Necessaire"
          labelWrap
          sub="Seus produtos"
          onClick={onOpenNecessaire}
        />
        <CollectionChip
          icon={<Circle size={23} strokeWidth={1.75} />}
          label="Atons"
          sub="Avalie e pontue"
          onClick={() => setRateSheetOpen(true)}
        />
      </div>

      <div className="jota-search-box">
        <Search size={16} strokeWidth={1.75} color="#9A9AA2" />
        <input
          className="jota-search-input"
          placeholder="Buscar produto, marca, preocupação..."
          value={query}
          onChange={(e) => setQuery(e.target.value)}
        />
        <button className="jota-search-filter-btn" onClick={() => setFiltersOpen((v) => !v)} aria-label="Filtros">
          <SlidersHorizontal size={17} strokeWidth={1.75} />
          {otherFilterCount > 0 && <span className="jota-filters-count">{otherFilterCount}</span>}
        </button>
      </div>

      {filtersOpen && (
        <div className="jota-filters-panel">
          <FilterGroup label="Preocupações de pele" count={concernFilters.length}>
            <div className="jota-filters-options">
              {SKIN_CONCERNS.map((c) => (
                <button
                  key={c}
                  className={`jota-recur-chip ${concernFilters.includes(c) ? "active" : ""}`}
                  onClick={() => toggleConcern(c)}
                >
                  {c}
                </button>
              ))}
            </div>
          </FilterGroup>

          <FilterGroup label="Ativos" hint="(ingredientes ativos)" count={ativoFilters.length}>
            <div className="jota-ativo-mode">
              <span className="jota-ativo-mode-label">Mostrar produtos que contêm</span>
              <div className="jota-ativo-mode-toggle">
                <button
                  className={ativoMatchMode === "algum" ? "active" : ""}
                  onClick={() => setAtivoMatchMode("algum")}
                >
                  Pelo menos um
                </button>
                <button
                  className={ativoMatchMode === "todos" ? "active" : ""}
                  onClick={() => setAtivoMatchMode("todos")}
                >
                  Todos
                </button>
              </div>
            </div>
            <div className="jota-filters-options">
              {[...PRODUCT_ACTIVES].sort((a, b) => a.name.localeCompare(b.name, "pt-BR")).map((a) => (
                <button
                  key={a.name}
                  className={`jota-recur-chip ${ativoFilters.includes(a.name) ? "active" : ""} ${a.pro ? "pro" : ""}`}
                  onClick={() => toggleAtivo(a.name)}
                  title={a.pro ? PRO_ACTIVE_LABEL : undefined}
                >
                  {a.name}
                  {a.pro && <AlertCircle size={11} strokeWidth={2.2} className="jota-ativo-pro-icon" />}
                </button>
              ))}
            </div>
            <div className="jota-ativo-legend">
              <AlertCircle size={11} strokeWidth={2.2} /> {PRO_ACTIVE_LABEL}
            </div>
          </FilterGroup>

          <FilterGroup label="Etapa" count={stepTypeFilter ? 1 : 0}>
            <div className="jota-filters-options">
              {STEP_TYPES.map((s) => (
                <button
                  key={s}
                  className={`jota-recur-chip ${stepTypeFilter === s ? "active" : ""}`}
                  onClick={() => setStepTypeFilter(stepTypeFilter === s ? null : s)}
                >
                  {s}
                </button>
              ))}
            </div>
          </FilterGroup>

          <FilterGroup label="Área de aplicação" count={bodyAreaFilter ? 1 : 0}>
            <div className="jota-filters-options">
              {BODY_AREAS.map((a) => (
                <button
                  key={a}
                  className={`jota-recur-chip ${bodyAreaFilter === a ? "active" : ""}`}
                  onClick={() => setBodyAreaFilter(bodyAreaFilter === a ? null : a)}
                >
                  {a}
                </button>
              ))}
            </div>
          </FilterGroup>

          <FilterGroup label="Período" count={periodFilter !== "todos" ? 1 : 0}>
            <div className="jota-filters-options">
              {[{ id: "todos", label: "Todos" }, { id: "dia", label: "Dia" }, { id: "tarde", label: "Tarde" }, { id: "noite", label: "Noite" }].map((o) => (
                <button
                  key={o.id}
                  className={`jota-recur-chip ${periodFilter === o.id ? "active" : ""}`}
                  onClick={() => setPeriodFilter(o.id)}
                >
                  {o.label}
                </button>
              ))}
            </div>
          </FilterGroup>

          <FilterGroup label="Preço" count={priceActive ? 1 : 0}>
            <PriceRangeSlider min={PRICE_MIN} max={PRICE_MAX} value={priceRange} onChange={setPriceRange} />
          </FilterGroup>

          <FilterGroup label="Tamanho" hint="(ml, mg, kg — depende do produto)" count={sizeFilter ? 1 : 0}>
            <div className="jota-filters-options">
              {SIZE_BRACKETS.map((b) => (
                <button
                  key={b.id}
                  className={`jota-recur-chip ${sizeFilter === b.id ? "active" : ""}`}
                  onClick={() => setSizeFilter(sizeFilter === b.id ? null : b.id)}
                >
                  {b.label}
                </button>
              ))}
            </div>
          </FilterGroup>

          <FilterGroup label="Marca" count={brandFilter ? 1 : 0}>
            <div className="jota-filters-options">
              {brands.map((b) => (
                <button
                  key={b}
                  className={`jota-recur-chip ${brandFilter === b ? "active" : ""}`}
                  onClick={() => setBrandFilter(brandFilter === b ? null : b)}
                >
                  {b}
                </button>
              ))}
            </div>
          </FilterGroup>

          <div className="jota-filters-actions">
            <button className="jota-filters-clear" onClick={clearFilters}>Limpar filtros</button>
            <button className="jota-filters-save" onClick={() => setFiltersOpen(false)}>Salvar filtros</button>
          </div>
        </div>
      )}

      <div className="jota-pgrid">
        {results.map((p) => (
          <ProductCard
            key={p.id}
            product={p}
            onOpen={setSelectedProduct}
            isFavorite={!!favorites[p.id]}
            onToggleFavorite={() => toggleFavorite(p.id)}
            tags={tagsFor(p.id)}
          />
        ))}
        {results.length === 0 && (
          <div className="jota-empty-steps">Nenhum produto encontrado com esses filtros.</div>
        )}
      </div>

      <div className="jota-suggest-footer">
        {suggestSent ? (
          <div className="jota-suggest-sent">Recebemos sua sugestão, obrigado! 🤍</div>
        ) : !suggestOpen ? (
          <button className="jota-suggest-link" onClick={() => setSuggestOpen(true)}>
            Não achou o produto? Sugira aqui
          </button>
        ) : (
          <div className="jota-suggest-form">
            <input
              className="jota-add-input jota-suggest-input"
              placeholder="Nome do produto"
              value={suggestDraft}
              autoFocus
              onChange={(e) => setSuggestDraft(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") sendSuggestion(); }}
            />
            <button className="jota-add-confirm" onClick={sendSuggestion} aria-label="Enviar sugestão">
              <Check size={13} strokeWidth={2} />
            </button>
          </div>
        )}
      </div>

      <ProductDetailSheet
        product={selectedProduct}
        onClose={() => setSelectedProduct(null)}
        myRating={selectedProduct ? myRatings[selectedProduct.id] : null}
        onRate={onRate}
        onSubmitReview={onSubmitReview}
        isFavorite={selectedProduct ? !!favorites[selectedProduct.id] : false}
        onToggleFavorite={() => selectedProduct && toggleFavorite(selectedProduct.id)}
        tags={selectedProduct ? tagsFor(selectedProduct.id) : null}
      />

      <RateProductsSheet
        open={rateSheetOpen}
        onClose={() => setRateSheetOpen(false)}
        myRatings={myRatings}
        onRate={onRate}
        onEarnPoints={onEarnPoints}
        favorites={favorites}
        onToggleFavorite={toggleFavorite}
        onViewProduct={(p) => { setRateSheetOpen(false); setSelectedProduct(p); }}
        routines={routines}
        tagsFor={tagsFor}
      />

      <ProductCollectionSheet
        open={favSheetOpen}
        onClose={() => setFavSheetOpen(false)}
        title="Favoritos"
        headIcon={<Heart size={18} strokeWidth={1.9} />}
        banner={{
          gradient: "linear-gradient(110deg, #FF6B6B 0%, #FF4D8D 55%, #C2185B 100%)",
          haloColor: "rgba(255,77,141,0.6)",
          icon: <Heart size={26} strokeWidth={1.75} />,
          cap: "Seus favoritos",
        }}
        membership={favorites}
        onToggle={toggleFavorite}
        productStatus={productStatus}
        onOpenProduct={(p) => { setFavSheetOpen(false); setSelectedProduct(p); }}
        emptyTitle="Nenhum favorito ainda"
        emptySub={'Toque em "Adicionar produto" ou no coração na página de um produto pra guardá-lo aqui.'}
      />

      <ProductCollectionSheet
        open={wishSheetOpen}
        onClose={() => setWishSheetOpen(false)}
        title="Desejos"
        headIcon={<Bookmark size={18} strokeWidth={1.9} />}
        banner={{
          gradient: "linear-gradient(110deg, #7C6FF0 0%, #9B6FE0 55%, #5E8BE0 100%)",
          haloColor: "rgba(124,111,240,0.6)",
          icon: <Bookmark size={24} strokeWidth={1.75} />,
          cap: "Seus desejos",
        }}
        membership={wishlist}
        onToggle={toggleWishlist}
        productStatus={productStatus}
        onOpenProduct={(p) => { setWishSheetOpen(false); setSelectedProduct(p); }}
        emptyTitle="Nenhum desejo ainda"
        emptySub={'Toque em "Adicionar produto" ou no marcador na página de um produto pra guardá-lo aqui.'}
      />
    </>
  );
}

const QUIZ_CONCERN_OPTIONS = [...SKIN_CONCERNS, "Não sei / Nenhuma em especial"];

const BODY_REGIONS = [
  "Rosto", "Região dos olhos", "Pescoço", "Colo", "Couro cabeludo", "Barriga",
  "Tórax", "Costas", "Braços", "Pernas", "Virilha", "Axilas", "Mãos", "Pés", "Glúteos",
  "Não sei / Não se aplica",
];

const ROUTINE_GOALS = [
  "Controlar oleosidade", "Clarear manchas", "Hidratar", "Acalmar a pele", "Melhorar acne",
  "Reduzir poros", "Uniformizar textura", "Prevenir sinais de idade", "Tratar linhas finas",
  "Melhorar olheiras", "Fortalecer a barreira da pele", "Cuidar do couro cabeludo",
  "Melhorar aparência corporal", "Montar uma rotina básica", "Montar uma rotina mais completa",
  "Ainda não sei bem",
];

const ROUTINE_PRODUCTS_USED = [
  "Sabonete ou gel de limpeza", "Hidratante", "Protetor solar", "Sérum", "Ácidos", "Retinol",
  "Vitamina C", "Produto clareador", "Produto antiacne", "Produto para cabelo ou couro cabeludo",
  "Produto corporal", "Nenhum ainda",
];

const REACTIVITY_OPTIONS = [
  "Sim, arde, coça ou fica vermelha com facilidade", "Às vezes",
  "Não, minha pele costuma tolerar bem os produtos", "Não sei",
];

const RISK_FLAGS = [
  "Gestante", "Lactante", "Em tratamento dermatológico", "Uso de medicamento controlado pra pele",
  "Alergia conhecida a algum produto", "Feridas abertas", "Queimaduras", "Descamação intensa",
  "Procedimento estético recente", "Nenhuma dessas",
];

const ROUTINE_TYPE_PREFS = [
  "Básica, com poucos passos", "Intermediária, com alguns ativos", "Completa, com tratamento direcionado",
  "Rotina barata e acessível", "Rotina para manhã", "Rotina para noite", "Rotina para quem tem pouco tempo",
  "Não sei ainda",
];

// Inclui identidades trans/não-binárias e opções de quem prefere não responder
// ou ainda está descobrindo — nenhuma delas é obrigatória pra usar o app.
const GENDER_OPTIONS = [
  "Mulher cisgênero", "Homem cisgênero", "Mulher trans", "Homem trans", "Não-binário",
  "Prefiro não dizer", "Não sei",
];

const BIRTH_MONTHS = [
  "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho",
  "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro",
];

const QUIZ_STEPS = [
  {
    id: "birthdate",
    title: "Quando é o seu aniversário?",
    sub: "Ajuda a gente a pensar em cuidados adequados pra sua idade",
    type: "date",
  },
  {
    id: "gender",
    title: "Como você se identifica?",
    sub: "Fica só entre a gente — ajuda a personalizar sua experiência",
    type: "single",
    options: GENDER_OPTIONS,
  },
  {
    id: "skinType",
    title: "Qual é o seu tipo de pele?",
    sub: "Se não souber, sem problema — escolha a última opção",
    type: "single",
    options: SKIN_TYPES,
    help: SKIN_TYPE_HELP,
  },
  {
    id: "concerns",
    title: "O que mais te incomoda hoje?",
    sub: "Pode escolher mais de uma opção",
    type: "multi",
    options: QUIZ_CONCERN_OPTIONS,
    exclusive: "Não sei / Nenhuma em especial",
  },
  {
    id: "priority",
    title: "Dessas, qual é sua principal prioridade?",
    sub: "Vamos focar nossa recomendação nisso primeiro",
    type: "single",
    getOptions: (a) => (a.concerns || []).filter((c) => c !== "Não sei / Nenhuma em especial"),
    skip: (a) => (a.concerns || []).filter((c) => c !== "Não sei / Nenhuma em especial").length <= 1,
  },
  {
    id: "bodyRegions",
    title: "Quais regiões do corpo mais te incomodam ou precisam de cuidado?",
    sub: "Pode escolher mais de uma",
    type: "multi",
    options: BODY_REGIONS,
    exclusive: "Não sei / Não se aplica",
  },
  {
    id: "goal",
    title: "Qual é o seu principal objetivo com a rotina?",
    type: "single",
    options: ROUTINE_GOALS,
  },
  {
    id: "productsUsed",
    title: "Você já usa algum desses produtos?",
    sub: "Pode escolher mais de um",
    type: "multi",
    options: ROUTINE_PRODUCTS_USED,
    exclusive: "Nenhum ainda",
  },
  {
    id: "frequency",
    title: "Com que frequência você cuida da pele?",
    type: "single",
    options: ["Raramente", "Algumas vezes por semana", "Diariamente"],
  },
  {
    id: "duration",
    title: "Há quanto tempo você pratica skincare?",
    type: "single",
    options: ["Menos de 6 meses", "6 meses a 2 anos", "Mais de 2 anos"],
  },
  {
    id: "reactivity",
    title: "Sua pele costuma reagir fácil?",
    type: "single",
    options: REACTIVITY_OPTIONS,
  },
  {
    id: "riskFlags",
    title: "Existe alguma condição importante pra considerar?",
    sub: "Isso ajuda a manter sua rotina segura — responda com sinceridade",
    type: "multi",
    options: RISK_FLAGS,
    exclusive: "Nenhuma dessas",
  },
  {
    id: "routinePref",
    title: "Qual tipo de rotina você prefere?",
    type: "single",
    options: ROUTINE_TYPE_PREFS,
  },
];

const LEVEL_SCORES = {
  frequency: { "Raramente": 0, "Algumas vezes por semana": 1, "Diariamente": 2 },
  duration: { "Menos de 6 meses": 0, "6 meses a 2 anos": 1, "Mais de 2 anos": 2 },
};

function computeSkinLevel(answers) {
  const freqScore = LEVEL_SCORES.frequency[answers.frequency] ?? 0;
  const durScore = LEVEL_SCORES.duration[answers.duration] ?? 0;
  const productsCount = (answers.productsUsed || []).filter((p) => p !== "Nenhum ainda").length;
  const routineScore = productsCount >= 4 ? 2 : productsCount >= 2 ? 1 : 0;
  const total = freqScore + durScore + routineScore;
  if (total <= 1) return "Básico";
  if (total <= 3) return "Intermediário";
  if (total <= 5) return "Avançado";
  return "Expert";
}

const LEVEL_ICONS = { "Básico": Sprout, "Intermediário": Leaf, "Avançado": Flame, "Expert": Gem };

// Tema visual por nível: mesma família de cores/ângulo do banner de rotina (céu).
// O trilho da barra usa o degradê do tema; o preenchido usa o mesmo degradê espelhado
// (ordem invertida), pra o progresso contrastar sem precisar de uma cor nova.
const DIARIO_LEVEL_THEME = {
  "Básico": { bg: "linear-gradient(110deg, #2D6FE0 0%, #4FBFDE 46%, #FF6FC0 100%)", halo: "rgba(255,111,192,0.6)", haloOp: 0.35, barA: "#2D6FE0", barB: "#FF6FC0", p: "18%" },
  "Intermediário": { bg: "linear-gradient(110deg, #1E8F5E 0%, #2FBFC2 55%, #50F0BE 100%)", halo: "rgba(80,240,190,0.65)", haloOp: 0.5, barA: "#1E8F5E", barB: "#50F0BE", p: "46%" },
  "Avançado": { bg: "linear-gradient(110deg, #6A3FBE 0%, #A05AFF 48%, #FFAA55 100%)", halo: "rgba(255,170,85,0.7)", haloOp: 0.7, barA: "#6A3FBE", barB: "#FFAA55", p: "74%" },
  "Expert": { bg: "linear-gradient(110deg, #23D7EB 0%, #6A45B8 55%, #4C2F94 100%)", halo: "rgba(35,215,235,0.75)", haloOp: 0.9, barA: "#23D7EB", barB: "#4C2F94", p: "100%" },
};

// Banner do Diário: substitui o céu por aqui — nível de cuidado (aura + ícone que
// já existem no app) + sequência de dias com a rotina em dia.
function DiarioLevelBanner({ profile }) {
  const level = profile && profile.level;
  if (!level) return null;
  const LevelIcon = LEVEL_ICONS[level] || Sprout;
  const theme = DIARIO_LEVEL_THEME[level] || DIARIO_LEVEL_THEME["Básico"];
  return (
    <div className="jota-diario-banner" style={{ background: theme.bg, "--barA": theme.barA, "--barB": theme.barB }}>
      <div className="jota-diario-banner-haze" />
      <span className="jota-diario-banner-seed">
        <span className="jota-diario-banner-halo" style={{ background: `radial-gradient(circle, ${theme.halo}, transparent 66%)`, opacity: theme.haloOp }} />
        <LevelIcon size={38} strokeWidth={1.6} />
      </span>
      <div className="jota-diario-banner-info">
        <span className="jota-diario-banner-cap">Seu nível em skincare</span>
        <span className="jota-diario-banner-lvl">{level}</span>
        <div className="jota-diario-banner-bar"><i style={{ width: theme.p }} /></div>
      </div>
    </div>
  );
}

// Banner colorido/glass da Necessaire — mesma linguagem do banner de nível do
// Diário (aura + vidro), só que mostrando quantos produtos estão guardados.
function NecessaireCountBanner({ count }) {
  return (
    <div className="jota-diario-banner" style={{ background: "linear-gradient(110deg, #FF8A65 0%, #FF6FA5 55%, #C86DD7 100%)" }}>
      <div className="jota-diario-banner-haze" />
      <span className="jota-diario-banner-seed">
        <span className="jota-diario-banner-halo" style={{ background: "radial-gradient(circle, rgba(255,111,165,0.6), transparent 66%)", opacity: 0.45 }} />
        <NecessaireIcon size={26} strokeWidth={1.75} />
      </span>
      <div className="jota-diario-banner-info">
        <span className="jota-diario-banner-cap">Sua necessaire</span>
        <span className="jota-diario-banner-lvl">{count} {count === 1 ? "produto" : "produtos"}</span>
      </div>
    </div>
  );
}

const EMPTY_QUIZ_ANSWERS = {
  birthdate: null, gender: null,
  skinType: null, concerns: [], priority: null, bodyRegions: [], goal: null,
  productsUsed: [], frequency: null, duration: null, reactivity: null,
  riskFlags: [], routinePref: null,
};

// Passo de data de nascimento do questionário — 3 selects (dia/mês/ano) em
// vez dos botões de opção, já que não dá pra listar todo dia como um botão.
function BirthdateStep({ value, onChange }) {
  const CURRENT_YEAR = new Date().getFullYear();
  const initial = value ? value.split("-").map(Number) : [null, null, null];
  // Estado local por campo — o valor combinado (YYYY-MM-DD) só existe quando os
  // 3 estão preenchidos, então guiar os selects por ele faria a seleção de um
  // campo sozinho "sumir" a cada troca (já que o combinado incompleto vira null).
  const [y, setY] = useState(initial[0]);
  const [m, setM] = useState(initial[1]);
  const [d, setD] = useState(initial[2]);

  const daysInMonth = (year, month) => (year && month ? new Date(year, month, 0).getDate() : 31);
  const maxDay = daysInMonth(y, m);

  useEffect(() => {
    if (y && m && d) {
      const clampedDay = Math.min(d, daysInMonth(y, m));
      onChange(`${y}-${String(m).padStart(2, "0")}-${String(clampedDay).padStart(2, "0")}`);
    } else {
      onChange(null);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [y, m, d]);

  return (
    <div className="jota-birthdate-row">
      <select className="jota-wizard-select" value={d || ""} onChange={(e) => setD(Number(e.target.value) || null)}>
        <option value="">Dia</option>
        {Array.from({ length: maxDay }, (_, i) => i + 1).map((n) => <option key={n} value={n}>{n}</option>)}
      </select>
      <select className="jota-wizard-select" value={m || ""} onChange={(e) => setM(Number(e.target.value) || null)}>
        <option value="">Mês</option>
        {BIRTH_MONTHS.map((name, i) => <option key={name} value={i + 1}>{name}</option>)}
      </select>
      <select className="jota-wizard-select" value={y || ""} onChange={(e) => setY(Number(e.target.value) || null)}>
        <option value="">Ano</option>
        {Array.from({ length: 96 }, (_, i) => CURRENT_YEAR - 5 - i).map((yr) => <option key={yr} value={yr}>{yr}</option>)}
      </select>
    </div>
  );
}

function ProfileWizardSheet({ open, onClose, initialAnswers, onComplete, editing = false }) {
  const [step, setStep] = useState(0);
  const [answers, setAnswers] = useState(initialAnswers);
  const [disclaimerOpen, setDisclaimerOpen] = useState(false);
  // Salva o que já está preenchido e fecha (útil quando a pessoa só edita a análise).
  const saveNow = () => {
    onComplete({ ...answers, level: computeSkinLevel(answers) });
  };

  useEffect(() => {
    if (open) {
      setStep(0);
      setAnswers(initialAnswers);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  const current = QUIZ_STEPS[step];
  const options = current.getOptions ? current.getOptions(answers) : current.options;

  const findNextIndex = (fromIndex, updatedAnswers, direction) => {
    let idx = fromIndex + direction;
    while (idx >= 0 && idx < QUIZ_STEPS.length) {
      const s = QUIZ_STEPS[idx];
      if (!s.skip || !s.skip(updatedAnswers)) return idx;
      idx += direction;
    }
    return idx;
  };

  const advanceOrComplete = (updatedAnswers) => {
    const next = findNextIndex(step, updatedAnswers, 1);
    if (next >= QUIZ_STEPS.length) {
      onComplete({ ...updatedAnswers, level: computeSkinLevel(updatedAnswers) });
    } else {
      setStep(next);
    }
  };

  const goBack = () => {
    const prev = findNextIndex(step, answers, -1);
    if (prev >= 0) setStep(prev);
  };

  const selectSingle = (opt) => {
    const updated = { ...answers, [current.id]: opt };
    setAnswers(updated);
    setTimeout(() => advanceOrComplete(updated), 300);
  };

  const toggleMulti = (opt) => {
    setAnswers((a) => {
      const list = a[current.id] || [];
      let next;
      if (current.exclusive) {
        if (opt === current.exclusive) {
          next = list.includes(opt) ? [] : [current.exclusive];
        } else {
          const withoutExclusive = list.filter((x) => x !== current.exclusive);
          next = withoutExclusive.includes(opt)
            ? withoutExclusive.filter((x) => x !== opt)
            : [...withoutExclusive, opt];
        }
      } else {
        next = list.includes(opt) ? list.filter((x) => x !== opt) : [...list, opt];
      }
      return { ...a, [current.id]: next };
    });
  };

  return (
    <div className={`jota-wizard-sheet ${open ? "open" : ""}`}>
      <div className="jota-wizard-head">
        {step > 0 && (
          <button className="jota-wizard-back" onClick={goBack} aria-label="Voltar">
            <ChevronLeft size={18} strokeWidth={2} />
          </button>
        )}
        <div className="jota-wizard-head-right">
          <button className="jota-chat-info-btn" onClick={() => setDisclaimerOpen(true)} aria-label="Aviso importante">
            <AlertCircle size={17} strokeWidth={1.8} />
          </button>
          <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
            <X size={16} />
          </button>
          {editing && (
            <button className="jota-sheet-close jota-wizard-save" onClick={saveNow} aria-label="Salvar">
              <Check size={16} strokeWidth={2.5} />
            </button>
          )}
        </div>
      </div>

      <div className="jota-wizard-hero">
        <JotaOrb size={60} />
      </div>

      <DisclaimerModal open={disclaimerOpen} onClose={() => setDisclaimerOpen(false)} />

      <div className="jota-rate-progress jota-wizard-progress">
        {QUIZ_STEPS.map((_, i) => (
          <span key={i} className={`jota-rate-progress-dot ${i === step ? "active" : ""} ${i < step ? "passed" : ""}`} />
        ))}
      </div>

      <div className="jota-wizard-body">
        <span className="jota-wizard-title">{current.title}</span>
        {current.sub && <span className="jota-wizard-sub">{current.sub}</span>}

        {current.type === "date" ? (
          <BirthdateStep
            value={answers.birthdate}
            onChange={(v) => setAnswers((a) => ({ ...a, birthdate: v }))}
          />
        ) : (
          <div className={`jota-wizard-options ${current.type === "multi" ? "multi" : "single"}`}>
            {options.map((opt) => {
              const selected = current.type === "multi"
                ? (answers[current.id] || []).includes(opt)
                : answers[current.id] === opt;
              return (
                <button
                  key={opt}
                  className={`jota-wizard-option ${selected ? "selected" : ""}`}
                  onClick={() => (current.type === "multi" ? toggleMulti(opt) : selectSingle(opt))}
                >
                  <span className="jota-wizard-option-title">{opt}</span>
                  {current.help?.[opt] && <span className="jota-wizard-option-help">{current.help[opt]}</span>}
                </button>
              );
            })}
          </div>
        )}

        {(current.type === "multi" || current.type === "date") && (
          <button
            className="jota-review-submit jota-wizard-continue"
            onClick={() => advanceOrComplete(answers)}
            disabled={
              current.type === "multi" ? (answers[current.id] || []).length === 0
                : !answers.birthdate
            }
          >
            Continuar
          </button>
        )}
      </div>
    </div>
  );
}

function ProfileSummaryCard({ profile, onEdit, onRedo }) {
  const priority = profile.priority || (profile.concerns || [])[0] || "—";
  const otherConcerns = (profile.concerns || []).filter(
    (c) => c !== priority && c !== "Não sei / Nenhuma em especial"
  );
  const noRoutineYet = (profile.productsUsed || []).length === 0
    || (profile.productsUsed || []).includes("Nenhum ainda");
  const ageLabel = (() => {
    if (!profile.birthdate) return "—";
    const b = new Date(profile.birthdate + "T00:00:00");
    const now = new Date();
    let age = now.getFullYear() - b.getFullYear();
    const m = now.getMonth() - b.getMonth();
    if (m < 0 || (m === 0 && now.getDate() < b.getDate())) age--;
    return age >= 0 && age < 130 ? `${age} anos` : "—";
  })();

  return (
    <div className="jota-card jota-profile-summary">
      <div className="jota-card-head">
        <span className="jota-card-title">Seu perfil</span>
      </div>

      <div className="jota-profile-facts">
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Idade</span>
          <span className="jota-profile-fact-value">{ageLabel}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Gênero</span>
          <span className="jota-profile-fact-value">{profile.gender || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Tipo de pele</span>
          <span className="jota-profile-fact-value">{profile.skinType || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Principal incômodo</span>
          <span className="jota-profile-fact-value">{priority}</span>
        </div>
        {otherConcerns.length > 0 && (
          <div className="jota-profile-fact">
            <span className="jota-profile-fact-label">Outras preocupações</span>
            <span className="jota-profile-fact-value">{otherConcerns.join(", ")}</span>
          </div>
        )}
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Região afetada</span>
          <span className="jota-profile-fact-value">{(profile.bodyRegions || []).join(", ") || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Objetivo principal</span>
          <span className="jota-profile-fact-value">{profile.goal || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Sensibilidade</span>
          <span className="jota-profile-fact-value">{profile.reactivity || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Frequência</span>
          <span className="jota-profile-fact-value">{profile.frequency || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Há quanto tempo</span>
          <span className="jota-profile-fact-value">{profile.duration || "—"}</span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Rotina atual</span>
          <span className="jota-profile-fact-value">
            {noRoutineYet ? "Ainda não tem rotina" : `${profile.productsUsed.length} tipo(s) de produto`}
          </span>
        </div>
        <div className="jota-profile-fact">
          <span className="jota-profile-fact-label">Preferência</span>
          <span className="jota-profile-fact-value">{profile.routinePref || "—"}</span>
        </div>
      </div>

      <div className="jota-profile-actions">
        <button className="jota-profile-action-btn" onClick={onEdit}>
          <Pencil size={13} strokeWidth={1.75} /> Editar
        </button>
        <button className="jota-profile-action-btn" onClick={onRedo}>
          <RotateCcw size={13} strokeWidth={1.75} /> Refazer análise
        </button>
      </div>
    </div>
  );
}

// Dias seguidos (contando de hoje pra trás) em que 100% dos passos agendados
// foram concluídos. Pra no primeiro dia sem nada agendado ou com passo pendente.
function currentStreak(routines, maxDays = 60) {
  let streak = 0;
  for (let i = 0; i < maxDays; i++) {
    const d = new Date();
    d.setDate(d.getDate() - i);
    const key = todayKey(d);
    let total = 0;
    let done = 0;
    routines.forEach((r) => {
      r.steps.forEach((s) => {
        if (isScheduledOn(s, d)) {
          total++;
          if (s.completions?.[key]) done++;
        }
      });
    });
    if (total === 0 || done < total) break;
    streak++;
  }
  return streak;
}

function WeekConsistencyStrip({ routines }) {
  const days = Array.from({ length: 7 }).map((_, i) => {
    const d = new Date();
    d.setDate(d.getDate() - (6 - i));
    return d;
  });

  const dayStats = days.map((d) => {
    const key = todayKey(d);
    let total = 0;
    let done = 0;
    routines.forEach((r) => {
      r.steps.forEach((s) => {
        if (isScheduledOn(s, d)) {
          total++;
          if (s.completions?.[key]) done++;
        }
      });
    });
    return { date: d, pct: total > 0 ? done / total : 0, isToday: key === todayKey() };
  });

  return (
    <div className="jota-week-strip">
      {dayStats.map((ds, i) => (
        <div key={i} className="jota-week-bar-col">
          <div className="jota-week-bar-track">
            <div className="jota-week-bar-fill" style={{ height: `${Math.round(ds.pct * 100)}%` }} />
          </div>
          <span className={`jota-week-bar-label ${ds.isToday ? "today" : ""}`}>
            {WEEKDAYS[ds.date.getDay()][0].toUpperCase()}
          </span>
        </div>
      ))}
    </div>
  );
}

// Tile de número (contagem) do painel de Evolução — mesmo ícone monocromático
// e cantos da necessaire/rotina, só que informativo (não é um botão).
function EvoStatTile({ icon, value, label }) {
  return (
    <div className="jota-evo-stat">
      <span className="jota-evo-stat-icon">{icon}</span>
      <span className="jota-evo-stat-value">{value}</span>
      <span className="jota-evo-stat-label">{label}</span>
    </div>
  );
}

function DiaryPostSheet({ open, onClose, onSave, productOptions, editingPost }) {
  const [title, setTitle] = useState("");
  const [photos, setPhotos] = useState([]);
  const [note, setNote] = useState("");
  const [selectedProducts, setSelectedProducts] = useState([]);
  const fileInputRef = useRef(null);

  useEffect(() => {
    if (open) {
      setTitle(editingPost?.title || "");
      setPhotos(editingPost?.photos || []);
      setNote(editingPost?.note || "");
      setSelectedProducts(editingPost?.products || []);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  const handlePhotoChange = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => setPhotos((p) => [...p, reader.result]);
    reader.readAsDataURL(file);
    e.target.value = "";
  };

  const removePhoto = (idx) => {
    setPhotos((p) => p.filter((_, i) => i !== idx));
  };

  const toggleProduct = (p) => {
    setSelectedProducts((list) => (list.includes(p) ? list.filter((x) => x !== p) : [...list, p]));
  };

  const handleSave = () => {
    onSave({ title: title.trim(), photos, note: note.trim(), products: selectedProducts });
  };

  return (
    <div className={`jota-diary-post-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><Camera size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">{editingPost ? "Editar registro" : "Registrar hoje"}</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>

      <div className="jota-diary-post-body">
        <input
          type="file"
          accept="image/*"
          capture="environment"
          ref={fileInputRef}
          style={{ display: "none" }}
          onChange={handlePhotoChange}
        />

        <span className="jota-avatar-label">Título</span>
        <input
          className="jota-diary-title-input"
          type="text"
          placeholder="Ex: Pele mais calma hoje"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          maxLength={80}
        />

        <div className="jota-diary-photo-row">
          {photos.map((p, idx) => (
            <div key={idx} className="jota-diary-photo-thumb">
              <img src={p} alt="" />
              <button className="jota-diary-photo-remove" onClick={() => removePhoto(idx)} aria-label="Remover foto">
                <X size={11} strokeWidth={2.5} />
              </button>
            </div>
          ))}
          <button className="jota-diary-photo-add" onClick={() => fileInputRef.current?.click()}>
            <Camera size={22} strokeWidth={1.6} />
            <span>{photos.length === 0 ? "Adicionar foto" : "Mais uma"}</span>
          </button>
        </div>

        <span className="jota-avatar-label">Como sua pele está hoje?</span>
        <textarea
          className="jota-diary-textarea"
          placeholder="Ex: Hoje minha pele acordou mais sensível e avermelhada..."
          value={note}
          onChange={(e) => setNote(e.target.value)}
          rows={4}
        />

        <span className="jota-avatar-label">Produtos usados hoje</span>
        {productOptions.length === 0 ? (
          <p className="jota-diary-hint">Adicione produtos numa rotina pra poder marcá-los aqui.</p>
        ) : (
          <div className="jota-filters-options">
            {productOptions.map((p) => (
              <button
                key={p}
                className={`jota-recur-chip ${selectedProducts.includes(p) ? "active" : ""}`}
                onClick={() => toggleProduct(p)}
              >
                {p}
              </button>
            ))}
          </div>
        )}

        <button
          className="jota-review-submit jota-diary-save"
          onClick={handleSave}
          disabled={photos.length === 0 && !note.trim()}
        >
          Salvar registro
        </button>
      </div>
    </div>
  );
}

function DiarioScreen({
  myRatings, favorites, toggleFavorite, onRate, onSubmitReview, routines, profile, setProfile,
  wizardOpen, setWizardOpen, userId, necessaire, productStatus, points, tagsFor, onOpenNecessaire, onOpenTestados,
  showToast,
}) {
  const [selectedProduct, setSelectedProduct] = useState(null);
  const [diaryPosts, setDiaryPosts] = useState([]);
  const [postSheetOpen, setPostSheetOpen] = useState(false);
  const [editingPost, setEditingPost] = useState(null);
  const [viewingPost, setViewingPost] = useState(null);
  const [compareMode, setCompareMode] = useState(false);
  const [compareIds, setCompareIds] = useState([]);
  const [visibleCount, setVisibleCount] = useState(5);
  const [diaryDisclaimerOpen, setDiaryDisclaimerOpen] = useState(false);
  const [resultadosOpen, setResultadosOpen] = useState(false);
  const [historicoOpen, setHistoricoOpen] = useState(false);
  const [evolucaoOpen, setEvolucaoOpen] = useState(false);
  const [wizardStartFresh, setWizardStartFresh] = useState(false);

  // Editar registros: gear (modo persistente, ícones sempre visíveis) OU deslizar
  // o registro pra esquerda (revela as mesmas ações na hora) — igual à Rotina.
  const [diaryEditMode, setDiaryEditMode] = useState(false);
  // Mesma largura da Rotina (116px) — reaproveita .jota-swipe-actions sem CSS novo.
  const DIARY_SWIPE_REVEAL = 116;
  const [diarySwipeOpenId, setDiarySwipeOpenId] = useState(null);
  const diarySwipeState = useRef({ id: null, startX: 0, startY: 0, dx: 0, axis: null });
  const diaryRowRefs = useRef({});

  const closeDiarySwipe = () => setDiarySwipeOpenId(null);
  const handleDiarySwipeDown = (e, postId) => {
    if (diaryEditMode) return;
    diarySwipeState.current = { id: postId, startX: e.clientX, startY: e.clientY, dx: 0, axis: null };
  };
  const handleDiarySwipeMove = (e, postId) => {
    const st = diarySwipeState.current;
    if (st.id !== postId) return;
    const dx = e.clientX - st.startX;
    const dy = e.clientY - st.startY;
    if (st.axis === null && (Math.abs(dx) > 6 || Math.abs(dy) > 6)) {
      st.axis = Math.abs(dx) > Math.abs(dy) ? "x" : "y";
    }
    if (st.axis !== "x") return;
    const base = diarySwipeOpenId === postId ? -DIARY_SWIPE_REVEAL : 0;
    const next = Math.max(-DIARY_SWIPE_REVEAL, Math.min(0, base + dx));
    st.dx = next;
    const el = diaryRowRefs.current[postId];
    if (el) el.style.transform = `translateX(${next}px)`;
  };
  const handleDiarySwipeUp = (e, postId) => {
    const st = diarySwipeState.current;
    if (st.id !== postId) return;
    const el = diaryRowRefs.current[postId];
    if (st.axis !== "x") { st.id = null; return; }
    const shouldOpen = st.dx < -DIARY_SWIPE_REVEAL * 0.4;
    if (el) el.style.transform = shouldOpen ? `translateX(-${DIARY_SWIPE_REVEAL}px)` : "translateX(0)";
    setDiarySwipeOpenId(shouldOpen ? postId : null);
    st.id = null;
  };
  useEffect(() => { if (diaryEditMode) setDiarySwipeOpenId(null); }, [diaryEditMode]);

  const routineProductOptions = [...new Set(routines.flatMap((r) => r.steps.map((s) => s.label)))];

  // Carrega os posts do diário do banco. As fotos moram no Storage
  // (bucket privado diary-photos) — a leitura usa URLs assinadas.
  const loadDiary = async () => {
    if (!userId) { setDiaryPosts([]); return; }
    const { data: posts, error } = await supabase
      .from("diary_posts")
      .select("id, title, note, products, created_at, diary_post_photos(id, storage_path, order_index)")
      .order("created_at", { ascending: false });
    if (error) {
      console.error("[J-OTA] load diário:", error.message);
      showToast?.("Não consegui carregar seus registros. Verifique a conexão.", () => loadDiary());
      return;
    }

    const sortedPhotos = (p) => (p.diary_post_photos || []).slice().sort((a, b) => a.order_index - b.order_index);
    const allPaths = (posts || []).flatMap((p) => sortedPhotos(p).map((f) => f.storage_path));
    const urlByPath = {};
    if (allPaths.length) {
      const { data: signed } = await supabase.storage.from("diary-photos").createSignedUrls(allPaths, 3600);
      (signed || []).forEach((s) => { if (s.signedUrl) urlByPath[s.path] = s.signedUrl; });
    }

    setDiaryPosts((posts || []).map((p) => ({
      id: p.id,
      title: p.title || "",
      date: new Date(p.created_at).toLocaleDateString("pt-BR", { day: "2-digit", month: "short", year: "numeric" }),
      time: new Date(p.created_at).toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" }),
      note: p.note || "",
      products: p.products || [],
      photos: sortedPhotos(p).map((f) => urlByPath[f.storage_path]).filter(Boolean),
      photoPaths: sortedPhotos(p).map((f) => ({ path: f.storage_path, url: urlByPath[f.storage_path] })),
    })));
  };

  useEffect(() => {
    loadDiary();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [userId]);

  const openNewPost = () => {
    setEditingPost(null);
    setPostSheetOpen(true);
  };

  const openEditPost = (post) => {
    setEditingPost(post);
    setViewingPost(null);
    setPostSheetOpen(true);
  };

  const savePost = async (data) => {
    setPostSheetOpen(false);
    const isEdit = !!editingPost;
    const postId = isEdit ? editingPost.id : crypto.randomUUID();
    setEditingPost(null);
    if (!userId) return;

    try {
      // Fotos novas chegam como dataURL; as que já existiam vêm como URL
      // assinada — casamos de volta pro caminho no Storage pelo editingPost.
      const existing = isEdit ? (editingPost.photoPaths || []) : [];
      const keptRows = [];
      const uploads = [];
      data.photos.forEach((photo, i) => {
        if (photo.startsWith("data:")) {
          uploads.push({ dataUrl: photo, order: i });
        } else {
          const meta = existing.find((m) => m.url === photo);
          if (meta) keptRows.push({ path: meta.path, order: i });
        }
      });

      if (isEdit) {
        const { error } = await supabase.from("diary_posts").update({ title: data.title || null, note: data.note, products: data.products }).eq("id", postId);
        if (error) throw error;
      } else {
        const { error } = await supabase.from("diary_posts").insert({ id: postId, user_id: userId, title: data.title || null, note: data.note, products: data.products });
        if (error) throw error;
      }

      // Remove do Storage as fotos que saíram do post na edição.
      const removedPaths = existing.filter((m) => !keptRows.find((k) => k.path === m.path)).map((m) => m.path);
      if (removedPaths.length) await supabase.storage.from("diary-photos").remove(removedPaths);

      // Sobe as fotos novas (caminho começa com o userId — regra do RLS).
      for (const up of uploads) {
        const blob = await (await fetch(up.dataUrl)).blob();
        const ext = blob.type.includes("png") ? "png" : "jpg";
        const path = `${userId}/${postId}/${crypto.randomUUID()}.${ext}`;
        const { error: upErr } = await supabase.storage.from("diary-photos").upload(path, blob, { contentType: blob.type || "image/jpeg" });
        if (upErr) { console.error("[J-OTA] upload foto:", upErr.message); continue; }
        keptRows.push({ path, order: up.order });
      }

      // Regrava as linhas de foto do post com a ordem final.
      await supabase.from("diary_post_photos").delete().eq("post_id", postId);
      if (keptRows.length) {
        await supabase.from("diary_post_photos").insert(
          keptRows.map((r) => ({ post_id: postId, storage_path: r.path, order_index: r.order }))
        );
      }

      await loadDiary();
    } catch (e) {
      console.error("[J-OTA] salvar post do diário:", e.message || e);
      loadDiary();
    }
  };

  const deletePost = async (id) => {
    const post = diaryPosts.find((p) => p.id === id);
    setDiaryPosts((posts) => posts.filter((p) => p.id !== id));
    setViewingPost(null);
    if (!userId) return;
    const paths = (post?.photoPaths || []).map((m) => m.path);
    if (paths.length) await supabase.storage.from("diary-photos").remove(paths);
    await supabase.from("diary_posts").delete().eq("id", id);
  };

  const toggleCompareSelect = (id) => {
    setCompareIds((ids) => {
      if (ids.includes(id)) return ids.filter((x) => x !== id);
      if (ids.length >= 2) return [ids[1], id];
      return [...ids, id];
    });
  };

  const hasProfile = !!profile;

  const handleWizardComplete = (result) => {
    setProfile(result);
    setWizardOpen(false);
  };

  // Linha de registro do diário — reaproveitada em Resultados (últimos) e
  // Histórico (lista completa paginada). Editar/excluir: gear (jota-item-actions,
  // sempre visível) ou deslizar pra esquerda (jota-swipe-actions, revela na hora) —
  // mesmo padrão dos passos da Rotina.
  const renderDiaryRow = (post) => (
    <div key={post.id} className="jota-swipe-wrap">
      {!diaryEditMode && (
        <span className="jota-swipe-actions">
          <button
            className="jota-swipe-action"
            onClick={() => { closeDiarySwipe(); openEditPost(post); }}
            aria-label="Editar registro"
          >
            <Pencil size={15} strokeWidth={2} />
          </button>
          <button
            className="jota-swipe-action"
            onClick={() => { closeDiarySwipe(); deletePost(post.id); }}
            aria-label="Excluir registro"
          >
            <Trash2 size={15} strokeWidth={2} />
          </button>
        </span>
      )}
      <div
        className="jota-diary-row"
        ref={(el) => { diaryRowRefs.current[post.id] = el; }}
        style={{ transform: diarySwipeOpenId === post.id ? `translateX(-${DIARY_SWIPE_REVEAL}px)` : "translateX(0)" }}
        onPointerDown={(e) => handleDiarySwipeDown(e, post.id)}
        onPointerMove={(e) => handleDiarySwipeMove(e, post.id)}
        onPointerUp={(e) => handleDiarySwipeUp(e, post.id)}
        onPointerCancel={(e) => handleDiarySwipeUp(e, post.id)}
        onClick={() => setViewingPost(post)}
      >
        <span className={`jota-diary-row-photo ${diaryEditMode ? "jota-diary-row-photo-compact" : ""}`}>
          {post.photos?.[0] ? (
            <img src={post.photos[0]} alt="" />
          ) : (
            <span className="jota-diary-thumb-empty"><BookOpen size={18} strokeWidth={1.75} /></span>
          )}
          {post.photos?.length > 1 && <span className="jota-diary-thumb-count">+{post.photos.length - 1}</span>}
        </span>
        <span className="jota-diary-row-info">
          <span className="jota-diary-row-title">{post.title || "Registro do dia"}</span>
          <span className="jota-diary-row-meta">{post.date} · {post.time}</span>
        </span>
        {diaryEditMode && (
          <span className="jota-item-actions">
            <button className="jota-item-action" onClick={(e) => { e.stopPropagation(); openEditPost(post); }} aria-label="Editar registro">
              <Pencil size={13} strokeWidth={2} />
            </button>
            <button className="jota-item-action" onClick={(e) => { e.stopPropagation(); deletePost(post.id); }} aria-label="Excluir registro">
              <Trash2 size={13} strokeWidth={2} />
            </button>
          </span>
        )}
      </div>
    </div>
  );

  return (
    <>
      <DiarioLevelBanner profile={profile} />

      <div className="jota-collection-row">
        <CollectionChip
          icon={<NecessaireIcon size={23} strokeWidth={1.75} />}
          label="Necessaire"
          labelWrap
          sub="Seus produtos"
          onClick={onOpenNecessaire}
        />
        <CollectionChip
          icon={<FlaskConical size={23} strokeWidth={1.75} />}
          label="Testados"
          sub="Seu feedback"
          onClick={onOpenTestados}
        />
        <CollectionChip
          icon={<Camera size={23} strokeWidth={1.75} />}
          label="Registros"
          sub="Seus resultados"
          onClick={() => setResultadosOpen(true)}
        />
        <CollectionChip
          icon={<TrendingUp size={23} strokeWidth={1.75} />}
          label="Evolução"
          sub="Seu progresso"
          onClick={() => setEvolucaoOpen(true)}
        />
      </div>

      {!hasProfile && (
        <div className="jota-skin-cta">
          <span className="jota-skin-cta-title">Análise de pele:</span>
          <p className="jota-skin-cta-sub">
            Conte um pouco sobre sua pele e seus hábitos para receber sugestões mais personalizadas. É rápido,
            simples e ajuda o J-OTA a indicar produtos e rotinas que combinam melhor com você.
          </p>
          <button className="jota-review-submit" onClick={() => setWizardOpen(true)}>Começar análise</button>
        </div>
      )}

      {hasProfile && (
        <ProfileSummaryCard
          profile={profile}
          onEdit={() => { setWizardStartFresh(false); setWizardOpen(true); }}
          onRedo={() => { setWizardStartFresh(true); setWizardOpen(true); }}
        />
      )}

      {hasProfile && (
        <DisclaimerBar onOpen={() => setDiaryDisclaimerOpen(true)} />
      )}

      <div className={`jota-product-sheet ${resultadosOpen ? "open" : ""}`}>
        {resultadosOpen && (
          <>
            <div className="jota-sheet-head">
              <span className="jota-sheet-head-icon"><Camera size={18} strokeWidth={1.9} /></span>
              <span className="jota-sheet-name">Registros</span>
              <button className="jota-sheet-close" onClick={() => setResultadosOpen(false)} aria-label="Fechar">
                <X size={16} />
              </button>
            </div>
            <div className="jota-pdetail-body">
              <button className="jota-diary-cta-banner" onClick={openNewPost}>
                <span className="jota-diary-cta-icon">
                  <Camera size={24} strokeWidth={1.75} />
                </span>
                <div className="jota-diary-cta-text">
                  <span className="jota-diary-cta-title">Faça um registro</span>
                  <span className="jota-diary-cta-sub">Foto, anotação e produtos usados</span>
                </div>
              </button>

              {diaryPosts.length === 0 ? (
                <div className="jota-empty" style={{ marginBottom: 18 }}>
                  <div className="jota-empty-title">Nenhum registro ainda</div>
                  <div className="jota-empty-sub">Seus registros de pele ao longo do tempo vão aparecer aqui</div>
                </div>
              ) : (
                <div className="jota-card jota-diary-rows-card">
                  <div className="jota-diary-history-head">
                    <span>Registros</span>
                    <button
                      className={`jota-icon-btn ${diaryEditMode ? "active" : ""}`}
                      onClick={() => setDiaryEditMode((v) => !v)}
                      aria-label={diaryEditMode ? "Concluir edição dos registros" : "Editar registros"}
                    >
                      <Settings size={15} strokeWidth={1.75} />
                    </button>
                  </div>
                  <div className="jota-diary-rows">
                    {diaryPosts.slice(0, 8).map(renderDiaryRow)}
                  </div>
                </div>
              )}
            </div>
          </>
        )}
      </div>

      <div className={`jota-product-sheet ${evolucaoOpen ? "open" : ""}`}>
        {evolucaoOpen && (
          <>
            <div className="jota-sheet-head">
              <span className="jota-sheet-head-icon"><TrendingUp size={18} strokeWidth={1.9} /></span>
              <span className="jota-sheet-name">Evolução</span>
              <button className="jota-sheet-close" onClick={() => setEvolucaoOpen(false)} aria-label="Fechar">
                <X size={16} />
              </button>
            </div>
            <div className="jota-pdetail-body">
              <CollectionCountBanner
                gradient="linear-gradient(110deg, #34C77B 0%, #2BB6A3 55%, #3D9BE0 100%)"
                haloColor="rgba(43,182,163,0.6)"
                icon={<TrendingUp size={24} strokeWidth={1.75} />}
                cap="Sua evolução"
                count={currentStreak(routines)}
                unit={["dia seguido", "dias seguidos"]}
              />
              <div className="jota-evo-grid">
                <EvoStatTile icon={<Flame size={20} strokeWidth={1.75} />} value={currentStreak(routines)} label="Dias seguidos" />
                <EvoStatTile icon={<Circle size={18} strokeWidth={1.75} />} value={points ?? 0} label="Atons conquistados" />
                <EvoStatTile icon={<Camera size={19} strokeWidth={1.75} />} value={diaryPosts.length} label="Registros no diário" />
                <EvoStatTile icon={<FlaskConical size={19} strokeWidth={1.75} />} value={Object.keys(productStatus || {}).length} label="Produtos testados" />
              </div>

              <div className="jota-diario-section-title jota-diario-week-title">Sua semana</div>
              <div className="jota-card">
                <WeekConsistencyStrip routines={routines} />
              </div>
            </div>
          </>
        )}
      </div>

      <div className={`jota-product-sheet ${historicoOpen ? "open" : ""}`}>
        {historicoOpen && (
          <>
            <div className="jota-sheet-head">
              <span className="jota-sheet-head-icon"><History size={18} strokeWidth={1.9} /></span>
              <span className="jota-sheet-name">Histórico</span>
              <button className="jota-sheet-close" onClick={() => setHistoricoOpen(false)} aria-label="Fechar">
                <X size={16} />
              </button>
            </div>
            <div className="jota-pdetail-body">
              {diaryPosts.length === 0 ? (
                <div className="jota-empty">
                  <div className="jota-empty-title">Nenhum registro ainda</div>
                  <div className="jota-empty-sub">Seus registros de pele ao longo do tempo vão aparecer aqui</div>
                </div>
              ) : (
                <>
                  <div className="jota-diary-rows">
                    {diaryPosts.slice(0, visibleCount).map(renderDiaryRow)}
                  </div>
                  {diaryPosts.length > visibleCount && (
                    <button className="jota-diary-more-btn" onClick={() => setVisibleCount((c) => c + 5)}>
                      Ver mais ({diaryPosts.length - visibleCount})
                    </button>
                  )}
                </>
              )}
            </div>
          </>
        )}
      </div>

      {viewingPost && (
        <div className="jota-diary-view-overlay" onClick={() => setViewingPost(null)}>
          <div className="jota-diary-view-card" onClick={(e) => e.stopPropagation()}>
            <button className="jota-sheet-close jota-diary-view-close" onClick={() => setViewingPost(null)} aria-label="Fechar">
              <X size={16} />
            </button>
            {viewingPost.photos?.length > 0 && (
              <div className="jota-diary-view-photos">
                {viewingPost.photos.map((photo, idx) => (
                  <img key={idx} src={photo} alt="" className="jota-diary-view-photo" />
                ))}
              </div>
            )}
            {viewingPost.title && <span className="jota-diary-view-title">{viewingPost.title}</span>}
            <span className="jota-diary-view-date">{viewingPost.date}{viewingPost.time ? ` · ${viewingPost.time}` : ""}</span>
            {viewingPost.note && <p className="jota-diary-view-note">{viewingPost.note}</p>}
            {viewingPost.products?.length > 0 && (
              <div className="jota-filters-options jota-diary-view-tags">
                {viewingPost.products.map((p) => <span key={p} className="jota-tag">{p}</span>)}
              </div>
            )}
            <div className="jota-diary-view-actions">
              <button className="jota-diary-edit-btn" onClick={() => openEditPost(viewingPost)}>
                <Pencil size={13} strokeWidth={1.75} /> Editar
              </button>
              <button className="jota-diary-delete-btn" onClick={() => deletePost(viewingPost.id)}>
                <Trash2 size={13} strokeWidth={1.75} /> Excluir
              </button>
            </div>
          </div>
        </div>
      )}

      {compareMode && compareIds.length === 2 && (
        <div className="jota-diary-view-overlay" onClick={() => setCompareIds([])}>
          <div className="jota-diary-compare-card" onClick={(e) => e.stopPropagation()}>
            <button className="jota-sheet-close jota-diary-view-close" onClick={() => setCompareIds([])} aria-label="Fechar">
              <X size={16} />
            </button>
            {compareIds.map((id) => {
              const post = diaryPosts.find((p) => p.id === id);
              return (
                <div key={id} className="jota-diary-compare-col">
                  {post.photos?.[0] ? <img src={post.photos[0]} alt="" /> : <div className="jota-diary-thumb-empty"><BookOpen size={18} /></div>}
                  <span>{post.date}</span>
                </div>
              );
            })}
          </div>
        </div>
      )}

      <ProductDetailSheet
        product={selectedProduct}
        onClose={() => setSelectedProduct(null)}
        myRating={selectedProduct ? myRatings[selectedProduct.id] : null}
        onRate={onRate}
        onSubmitReview={onSubmitReview}
        isFavorite={selectedProduct ? !!favorites[selectedProduct.id] : false}
        onToggleFavorite={() => selectedProduct && toggleFavorite(selectedProduct.id)}
        tags={selectedProduct ? tagsFor(selectedProduct.id) : null}
      />

      <ProfileWizardSheet
        open={wizardOpen}
        onClose={() => setWizardOpen(false)}
        initialAnswers={wizardStartFresh ? EMPTY_QUIZ_ANSWERS : (profile || EMPTY_QUIZ_ANSWERS)}
        onComplete={handleWizardComplete}
        editing={hasProfile && !wizardStartFresh}
      />

      <DiaryPostSheet
        open={postSheetOpen}
        onClose={() => { setPostSheetOpen(false); setEditingPost(null); }}
        onSave={savePost}
        productOptions={routineProductOptions}
        editingPost={editingPost}
      />

      <DisclaimerModal open={diaryDisclaimerOpen} onClose={() => setDiaryDisclaimerOpen(false)} />
    </>
  );
}

function PerfilScreen({ userName, userEmail, onLogout, onUpdateName }) {
  const [saindo, setSaindo] = useState(false);
  const [editingName, setEditingName] = useState(false);
  const [nameDraft, setNameDraft] = useState(userName || "");
  const [saved, setSaved] = useState(false);

  useEffect(() => {
    if (!editingName) setNameDraft(userName || "");
  }, [userName, editingName]);

  const handleLogout = async () => {
    setSaindo(true);
    try {
      await onLogout();
    } finally {
      setSaindo(false);
    }
  };

  const saveName = async () => {
    const trimmed = nameDraft.trim();
    if (!trimmed) return;
    setEditingName(false);
    await onUpdateName(trimmed);
    setSaved(true);
    setTimeout(() => setSaved(false), 2000);
  };

  const cancelEditName = () => {
    setNameDraft(userName || "");
    setEditingName(false);
  };

  return (
    <>
      <div className="jota-eyebrow">PERFIL</div>
      <div className="jota-h1">{userName || "Sua conta"}</div>
      <div className="jota-sub">{userEmail || "Conta, preferências e conexão com clínica J-OTA."}</div>

      <div className="jota-card">
        <div className="jota-card-head">
          <span className="jota-card-title">Como você gostaria de ser chamado?</span>
        </div>
        {editingName ? (
          <div className="jota-profile-name-edit">
            <input
              className="jota-add-input"
              value={nameDraft}
              autoFocus
              onChange={(e) => setNameDraft(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") saveName(); if (e.key === "Escape") cancelEditName(); }}
              onBlur={saveName}
              placeholder="Seu nome"
            />
            <button className="jota-add-confirm" onClick={saveName} aria-label="Salvar nome">
              <Check size={13} strokeWidth={2} />
            </button>
          </div>
        ) : (
          <button className="jota-profile-name-display" onClick={() => setEditingName(true)}>
            <span>{userName || "Adicionar nome"}</span>
            <Pencil size={13} strokeWidth={1.75} />
          </button>
        )}
        {saved && <span className="jota-profile-name-saved">Salvo!</span>}
      </div>

      <div className="jota-empty">
        <div className="jota-empty-title">Mais preferências em breve</div>
        <div className="jota-empty-sub">Dados, preferências, conexão com clínica</div>
      </div>
      <button className="jota-logout-btn" onClick={handleLogout} disabled={saindo}>
        {saindo ? "Saindo…" : "Sair da conta"}
      </button>
    </>
  );
}


// Lista de ações que rendem Atons e quanto cada uma vale. Conforme o app ganha
// novas formas de pontuar, é só adicionar um item aqui — a tela "Valores Atons"
// reflete automaticamente.
const ATONS_VALUES = [
  {
    icon: Star,
    label: "Avaliar um produto",
    amount: 1,
    desc: "Toda vez que você dá uma nota (1 a 5 estrelas) a um produto.",
  },
  {
    icon: Gift,
    label: "Bônus de boas-vindas",
    amount: 100,
    desc: "Só na primeira vez, quando você cria sua conta no J-OTA.",
  },
];

function AtonsValuesSheet({ open, onClose }) {
  return (
    <div className={`jota-atons-values-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><Gift size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">Valores Atons</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>
      <div className="jota-points-body">
        <p className="jota-atons-values-intro">
          Assim que a gente criar novas formas de ganhar Atons, elas aparecem aqui.
        </p>
        {ATONS_VALUES.map((v) => (
          <div key={v.label} className="jota-atons-value-row">
            <span className="jota-atons-value-icon">
              <v.icon size={16} strokeWidth={2} />
            </span>
            <span className="jota-atons-value-text">
              <span className="jota-atons-value-label">{v.label}</span>
              <span className="jota-atons-value-desc">{v.desc}</span>
            </span>
            <span className="jota-atons-value-amount">+{v.amount}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Meta de nível é um placeholder (a cada 150 Atons) até definirmos faixas/recompensas reais.
function AtonsBanner({ points }) {
  const nextTarget = Math.ceil((points + 1) / 150) * 150;
  const remaining = Math.max(0, nextTarget - points);
  return (
    <div className="jota-atons-banner">
      <div className="jota-atons-haze" />
      <span className="jota-atons-star" style={{ top: "24%", left: "44%", animationDelay: "-1s" }} />
      <span className="jota-atons-star" style={{ top: "66%", left: "56%", animationDelay: "-3s" }} />
      <span className="jota-atons-star" style={{ top: "38%", left: "66%", animationDelay: "-4.5s" }} />
      <div className="jota-atons-num-wrap">
        <span className="jota-atons-num-anchor">
          <span className="jota-atons-glow" />
          <span className="jota-atons-num">{points}</span>
          <span className="jota-atons-orbit"><span className="jota-atons-dot" /></span>
        </span>
        <span className="jota-atons-unit">Atons</span>
      </div>
      <div className="jota-atons-txt">
        <span className="jota-atons-msg">Faltam {remaining} Atons<br />pro próximo nível</span>
        <span className="jota-atons-target">{nextTarget}</span>
      </div>
    </div>
  );
}

function PointsSheet({ open, onClose, points, history }) {
  const [selectedMonth, setSelectedMonth] = useState(() => monthKey(new Date().toISOString()));
  const [valuesOpen, setValuesOpen] = useState(false);

  useEffect(() => {
    if (open) setSelectedMonth(monthKey(new Date().toISOString()));
  }, [open]);

  // Últimos 7 dias (hoje incluído) — mesmo recorte usado no gráfico semanal do Diário.
  const weekDays = Array.from({ length: 7 }).map((_, i) => {
    const d = new Date();
    d.setDate(d.getDate() - (6 - i));
    const key = todayKey(d);
    const total = history
      .filter((h) => h.createdAt && todayKey(new Date(h.createdAt)) === key)
      .reduce((sum, h) => sum + h.amount, 0);
    return { date: d, total, isToday: key === todayKey() };
  });
  const weekMax = Math.max(1, ...weekDays.map((d) => d.total));

  // Meses com Atons + mês atual (mesmo vazio), do mais recente pro mais antigo.
  const monthKeysSet = new Set(history.map((h) => monthKey(h.createdAt)).filter(Boolean));
  monthKeysSet.add(monthKey(new Date().toISOString()));
  const monthKeys = Array.from(monthKeysSet).sort((a, b) => b.localeCompare(a));

  const filteredHistory = history.filter((h) => monthKey(h.createdAt) === selectedMonth);
  const monthTotal = filteredHistory.reduce((sum, h) => sum + h.amount, 0);

  return (
    <div className={`jota-points-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><Sparkle size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">Seus Atons</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>
      <div className="jota-points-body">
        <AtonsBanner points={points} />

        <button className="jota-points-values-link" onClick={() => setValuesOpen(true)}>
          Valores Atons
        </button>

        <div className="jota-points-section-title">Últimos 7 dias</div>
        <div className="jota-week-strip jota-points-week-wrap">
          {weekDays.map((d, i) => (
            <div key={i} className="jota-week-bar-col">
              <span className="jota-week-bar-count">{d.total > 0 ? d.total : ""}</span>
              <div className="jota-week-bar-track">
                <div className="jota-week-bar-fill" style={{ height: `${Math.round((d.total / weekMax) * 100)}%` }} />
              </div>
              <span className={`jota-week-bar-label ${d.isToday ? "today" : ""}`}>
                {WEEKDAYS[d.date.getDay()][0].toUpperCase()}
              </span>
            </div>
          ))}
        </div>

        <div className="jota-points-month-row">
          <span className="jota-points-history-title">Histórico</span>
          <div className="jota-points-month-select">
            {monthKeys.map((mk) => (
              <button
                key={mk}
                className={`jota-reviews-chip ${mk === selectedMonth ? "active" : ""}`}
                onClick={() => setSelectedMonth(mk)}
              >
                {monthLabel(mk)}
              </button>
            ))}
          </div>
        </div>
        <div className="jota-points-month-total">
          {monthTotal > 0 ? `+${monthTotal} Atons nesse mês` : "Nenhum Aton nesse mês"}
        </div>

        {filteredHistory.length === 0 ? (
          <div className="jota-empty-steps">Nenhum Atom nesse mês.</div>
        ) : (
          filteredHistory.map((h) => (
            <div key={h.id} className="jota-points-entry">
              <span className="jota-points-entry-text">
                <span className="jota-points-entry-desc">{h.description}</span>
                <span className="jota-points-entry-date">{h.date}</span>
              </span>
              <span className="jota-points-entry-amount">+{h.amount}</span>
            </div>
          ))
        )}
      </div>

      <AtonsValuesSheet open={valuesOpen} onClose={() => setValuesOpen(false)} />
    </div>
  );
}

function NotificationsSheet({ open, onClose, notifications, onToggleRead, onMarkAllRead }) {
  const hasUnread = notifications.some((n) => !n.read);

  return (
    <div className={`jota-notif-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><Bell size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">Notificações</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>
      <div className="jota-notif-body">
        {notifications.length > 0 && (
          <button className="jota-notif-markall" onClick={onMarkAllRead} disabled={!hasUnread}>
            Marcar todas como lidas
          </button>
        )}
        {notifications.length === 0 ? (
          <div className="jota-empty-steps">Nenhuma notificação por enquanto.</div>
        ) : (
          notifications.map((n) => (
            <button
              key={n.id}
              className={`jota-notif-item ${n.read ? "read" : "unread"}`}
              onClick={() => onToggleRead(n.id)}
            >
              <span className="jota-notif-dot" />
              <span className="jota-notif-text">
                <span className="jota-notif-title">{n.title}</span>
                <span className="jota-notif-message">{n.message}</span>
                <span className="jota-notif-date">{n.date}</span>
              </span>
            </button>
          ))
        )}
      </div>
    </div>
  );
}

function ChatHistorySheet({ open, onClose, conversations, onOpenConversation, onNewConversation }) {
  return (
    <div className={`jota-chathist-sheet ${open ? "open" : ""}`}>
      <div className="jota-sheet-head">
        <span className="jota-sheet-head-icon"><MessageCircle size={18} strokeWidth={1.9} /></span>
        <span className="jota-sheet-name">Conversas</span>
        <button className="jota-sheet-close" onClick={onClose} aria-label="Fechar">
          <X size={16} />
        </button>
      </div>
      <div className="jota-notif-body">
        <button className="jota-chathist-new" onClick={onNewConversation}>
          <SquarePen size={15} strokeWidth={2} /> Nova conversa
        </button>
        {conversations.length === 0 ? (
          <div className="jota-empty-steps">Nenhuma conversa anterior ainda.</div>
        ) : (
          conversations.map((c) => (
            <button key={c.id} className="jota-notif-item" onClick={() => onOpenConversation(c.id)}>
              <span className="jota-notif-dot" />
              <span className="jota-notif-text">
                <span className="jota-notif-title">{c.title || "Conversa"}</span>
                <span className="jota-notif-date">{formatRelativeDate(c.updated_at)}</span>
              </span>
            </button>
          ))
        )}
      </div>
    </div>
  );
}

function GoogleIcon() {
  return (
    <svg width="17" height="17" viewBox="0 0 18 18">
      <path fill="#4285F4" d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z" />
      <path fill="#34A853" d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z" />
      <path fill="#FBBC05" d="M3.964 10.71c-.18-.54-.282-1.117-.282-1.71s.102-1.17.282-1.71V4.958H.957C.347 6.173 0 7.548 0 9s.348 2.827.957 4.042l3.007-2.332z" />
      <path fill="#EA4335" d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z" />
    </svg>
  );
}

// Traduz os erros mais comuns do Supabase Auth pra português.
function traduzErroAuth(err) {
  const msg = (err?.message || "").toLowerCase();
  if (msg.includes("invalid login credentials")) return "E-mail ou senha incorretos.";
  if (msg.includes("email not confirmed")) return "Confirme seu e-mail antes de entrar (veja sua caixa de entrada).";
  if (msg.includes("user already registered")) return "Esse e-mail já tem conta. Tente entrar.";
  if (msg.includes("password should be at least")) return "A senha precisa ter pelo menos 8 caracteres.";
  if (msg.includes("unable to validate email") || msg.includes("invalid email")) return "E-mail inválido.";
  if (msg.includes("for security purposes") || msg.includes("rate limit")) return "Muitas tentativas. Espere um instante e tente de novo.";
  return err?.message || "Algo deu errado. Tente novamente.";
}

// Aviso do login/cadastro (erro, "conta criada", etc.) — cantos arredondados,
// ícone que combina com o tema e letra cinza, no mesmo padrão do resto do app.
function AuthMessage({ type = "info", children }) {
  const Icon = type === "error" ? AlertCircle : type === "success" ? Check : Info;
  return (
    <div className={`jota-auth-msg jota-auth-msg-${type}`}>
      <Icon size={15} strokeWidth={2} />
      <span>{children}</span>
    </div>
  );
}

function LoginScreen({ onAuthed }) {
  const [mode, setMode] = useState("entrar");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const [notice, setNotice] = useState("");
  const [acceptedTerms, setAcceptedTerms] = useState(false);

  const submit = async () => {
    setError("");
    setNotice("");
    if (mode === "recuperar") {
      if (!email.trim()) { setError("Digite seu e-mail."); return; }
      setBusy(true);
      try {
        // O link do e-mail volta pra cá; a app detecta o evento PASSWORD_RECOVERY
        // e mostra a tela de nova senha. (O domínio precisa estar na allowlist de
        // Redirect URLs do Supabase Auth.)
        const { error } = await supabase.auth.resetPasswordForEmail(email.trim(), {
          redirectTo: window.location.origin,
        });
        if (error) throw error;
        // Mensagem neutra de propósito: não revela se o e-mail existe (segurança).
        setNotice("Se houver uma conta vinculada a este e-mail, você receberá um link para criar uma nova senha. Confira sua caixa de entrada e a pasta de spam.");
      } catch (e) {
        setError(traduzErroAuth(e));
      } finally {
        setBusy(false);
      }
      return;
    }
    if (!email.trim() || !password) {
      setError("Preencha e-mail e senha.");
      return;
    }
    if (mode === "criar" && password !== confirm) {
      setError("As senhas não coincidem.");
      return;
    }
    if (mode === "criar" && !acceptedTerms) {
      setError("Você precisa aceitar os termos de uso e a política de privacidade para criar a conta.");
      return;
    }
    setBusy(true);
    try {
      if (mode === "entrar") {
        const { error } = await supabase.auth.signInWithPassword({
          email: email.trim(),
          password,
        });
        if (error) throw error;
        onAuthed({ isNew: false });
      } else {
        // O nome é pedido no onboarding (WelcomeNameScreen), não aqui.
        const { data, error } = await supabase.auth.signUp({
          email: email.trim(),
          password,
        });
        if (error) throw error;
        if (!data.session) {
          // Confirmação de e-mail está ligada no projeto.
          setNotice("Conta criada! Enviamos um link de confirmação pro seu e-mail. Confirme e depois entre.");
          setMode("entrar");
        } else {
          onAuthed({ isNew: true });
        }
      }
    } catch (e) {
      setError(traduzErroAuth(e));
    } finally {
      setBusy(false);
    }
  };

  // Login social: redireciona pro Google, que volta pro próprio app já
  // autenticado (o onAuthStateChange global cuida do resto).
  const signInWithGoogle = async () => {
    setError("");
    setNotice("");
    setBusy(true);
    const { error } = await supabase.auth.signInWithOAuth({
      provider: "google",
      options: { redirectTo: window.location.origin },
    });
    if (error) { setError(traduzErroAuth(error)); setBusy(false); }
  };

  return (
    <div className="jota-onboard-screen jota-glass-login">
      <div className="jota-glass-bg" aria-hidden="true">
        <span className="jota-glass-blob jota-glass-blob-a" />
        <span className="jota-glass-blob jota-glass-blob-b" />
      </div>

      <div className="jota-glass-hero">
        <JotaOrb size={72} />
      </div>

      <div className="jota-glass-card">
        <img src={LOGO_SRC} alt="J-OTA" className="jota-onboard-logo" />

        <input
          className="jota-onboard-input"
          type="email"
          autoComplete="email"
          placeholder="E-mail"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && submit()}
        />
        {mode !== "recuperar" && (
          <input
            className="jota-onboard-input"
            type="password"
            autoComplete={mode === "entrar" ? "current-password" : "new-password"}
            placeholder="Senha"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && submit()}
          />
        )}
        {mode === "criar" && (
          <input
            className="jota-onboard-input"
            type="password"
            autoComplete="new-password"
            placeholder="Confirmar senha"
            value={confirm}
            onChange={(e) => setConfirm(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && submit()}
          />
        )}

        {mode === "entrar" && (
          <button type="button" className="jota-forgot-link" onClick={() => { setError(""); setNotice(""); setMode("recuperar"); }}>
            Esqueci minha senha
          </button>
        )}

        {mode === "recuperar" && (
          <p className="jota-recover-hint">Digite o e-mail da sua conta e enviamos um link pra você criar uma nova senha.</p>
        )}

        {mode === "criar" && (
          <label className="jota-onboard-terms">
            <input
              type="checkbox"
              checked={acceptedTerms}
              onChange={(e) => setAcceptedTerms(e.target.checked)}
            />
            <span>
              Li e aceito os{" "}
              <button type="button" className="jota-terms-link" onClick={() => setNotice("Os termos de uso completos serão disponibilizados em breve.")}>termos de uso</button>
              {" "}e a{" "}
              <button type="button" className="jota-terms-link" onClick={() => setNotice("A política de privacidade completa será disponibilizada em breve.")}>política de privacidade</button>.
            </span>
          </label>
        )}

        {error && <AuthMessage type="error">{error}</AuthMessage>}
        {notice && <AuthMessage type="info">{notice}</AuthMessage>}

        <button className="jota-review-submit jota-onboard-primary" onClick={submit} disabled={busy}>
          {busy ? "Aguarde…" : mode === "entrar" ? "Entrar" : mode === "criar" ? "Criar conta" : "Enviar link de recuperação"}
        </button>

        {mode === "recuperar" && (
          <button type="button" className="jota-forgot-link jota-recover-back" onClick={() => { setError(""); setNotice(""); setMode("entrar"); }}>
            Voltar ao login
          </button>
        )}

        {mode !== "recuperar" && (
          <>
            <div className="jota-onboard-divider"><span>ou</span></div>
            <button className="jota-onboard-social" onClick={signInWithGoogle} disabled={busy}>
              <GoogleIcon /> <span>Continuar com Google</span>
            </button>
          </>
        )}

        <div className="jota-login-footer">
          <button
            className={`jota-login-footer-btn ${mode === "entrar" ? "active" : ""}`}
            onClick={() => { setError(""); setNotice(""); if (mode !== "entrar") setMode("entrar"); else submit(); }}
          >
            <Fingerprint size={22} strokeWidth={1.75} />
            <span>Entrar</span>
          </button>
          <span className="jota-login-footer-div" />
          <button
            className={`jota-login-footer-btn ${mode === "criar" ? "active" : ""}`}
            onClick={() => { setError(""); setNotice(""); if (mode !== "criar") setMode("criar"); else submit(); }}
          >
            <UserPlus size={22} strokeWidth={1.75} />
            <span>Criar conta</span>
          </button>
        </div>
      </div>
    </div>
  );
}

// Tela de definir nova senha — mostrada quando a pessoa volta pelo link de
// recuperação (evento PASSWORD_RECOVERY). Reaproveita o visual do login.
function ResetPasswordScreen({ onDone }) {
  const [password, setPassword] = useState("");
  const [confirm, setConfirm] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const [done, setDone] = useState(false);

  const submit = async () => {
    setError("");
    if (password.length < 8) { setError("A senha precisa ter pelo menos 8 caracteres."); return; }
    if (password !== confirm) { setError("As senhas não coincidem."); return; }
    setBusy(true);
    try {
      const { error } = await supabase.auth.updateUser({ password });
      if (error) throw error;
      setDone(true);
      setTimeout(() => onDone(), 1500);
    } catch (e) {
      setError(traduzErroAuth(e));
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="jota-onboard-screen jota-glass-login">
      <div className="jota-glass-bg" aria-hidden="true">
        <span className="jota-glass-blob jota-glass-blob-a" />
        <span className="jota-glass-blob jota-glass-blob-b" />
      </div>
      <div className="jota-glass-hero">
        <JotaOrb size={72} />
      </div>
      <div className="jota-glass-card">
        <img src={LOGO_SRC} alt="J-OTA" className="jota-onboard-logo" />
        <div className="jota-reset-title">Criar nova senha</div>
        {done ? (
          <AuthMessage type="success">Senha atualizada! Entrando…</AuthMessage>
        ) : (
          <>
            <input
              className="jota-onboard-input"
              type="password"
              autoComplete="new-password"
              placeholder="Nova senha"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && submit()}
            />
            <input
              className="jota-onboard-input"
              type="password"
              autoComplete="new-password"
              placeholder="Confirmar nova senha"
              value={confirm}
              onChange={(e) => setConfirm(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && submit()}
            />
            {error && <AuthMessage type="error">{error}</AuthMessage>}
            <button className="jota-review-submit jota-onboard-primary" onClick={submit} disabled={busy}>
              {busy ? "Salvando…" : "Salvar nova senha"}
            </button>
          </>
        )}
      </div>
    </div>
  );
}

// Primeira tela depois de criar a conta: boas-vindas + a pessoa diz como quer
// ser chamada (o nome vira o da conta; editável depois no Perfil).
function WelcomeNameScreen({ onBack, onContinue }) {
  const [name, setName] = useState("");
  return (
    <div className="jota-onboard-screen jota-onboard-welcome">
      {onBack && (
        <button className="jota-onboard-back" onClick={onBack} aria-label="Voltar">
          <ChevronLeft size={22} strokeWidth={2} />
        </button>
      )}
      <JotaOrb size={100} />
      <span className="jota-onboard-welcome-hi">Bem-vindo(a)!</span>
      <h1 className="jota-onboard-title jota-onboard-welcome-title">Sou o J-OTA!</h1>
      <p className="jota-onboard-text">Seu Assistente Inteligente de Beleza.</p>
      <span className="jota-onboard-q">Como você gostaria de ser chamado(a)?</span>
      <input
        className="jota-onboard-input"
        type="text"
        maxLength={40}
        autoComplete="given-name"
        placeholder="Escreva aqui"
        value={name}
        autoFocus
        onChange={(e) => setName(e.target.value)}
        onKeyDown={(e) => { if (e.key === "Enter" && name.trim()) onContinue(name.trim()); }}
      />
      <button className="jota-review-submit" disabled={!name.trim()} onClick={() => onContinue(name.trim())}>
        Continuar!
      </button>
    </div>
  );
}

// Segunda tela: convite pra fazer a análise de pele (ou deixar pra depois).
function AnalysisInviteScreen({ onBack, onStart, onLater }) {
  return (
    <div className="jota-onboard-screen jota-onboard-welcome">
      {onBack && (
        <button className="jota-onboard-back" onClick={onBack} aria-label="Voltar">
          <ChevronLeft size={22} strokeWidth={2} />
        </button>
      )}
      <JotaOrb size={100} />
      <h1 className="jota-onboard-title">Vamos começar?</h1>
      <p className="jota-onboard-text">
        Faça sua análise para eu conhecer melhor você e sua pele. Assim, posso indicar rotinas, produtos e muito mais.
      </p>
      <button className="jota-review-submit" onClick={onStart}>Fazer minha análise!</button>
      <button className="jota-onboard-secondary" onClick={onLater}>Fazer depois</button>
    </div>
  );
}

function buildSuggestedRoutines(profile) {
  const levelStepCounts = { "Básico": 2, "Intermediário": 3, "Avançado": 4, "Expert": 5 };
  const targetPerPeriod = levelStepCounts[profile.level] || 3;

  const scoreProduct = (p) => {
    const concernMatches = p.concerns.filter((c) => (profile.concerns || []).includes(c)).length;
    const typeMatch = p.skinTypes?.includes(profile.skinType) ? 1 : 0;
    return concernMatches * 2 + typeMatch + p.rating;
  };

  const pickTop = (pool, n) => [...pool].sort((a, b) => scoreProduct(b) - scoreProduct(a)).slice(0, n);

  const morningPool = PRODUCT_CATALOG.filter((p) => p.period === "dia" || p.period === "ambos");
  const nightPool = PRODUCT_CATALOG.filter((p) => p.period === "noite" || p.period === "ambos");

  return {
    morningPicks: pickTop(morningPool, targetPerPeriod),
    nightPicks: pickTop(nightPool, targetPerPeriod),
  };
}

function RoutineSuggestionScreen({ profile, onAccept, onSkip }) {
  const { morningPicks, nightPicks } = buildSuggestedRoutines(profile);

  return (
    <div className="jota-onboard-screen jota-onboard-routine-screen">
      <JotaOrb size={64} />
      <h1 className="jota-onboard-title">Baseado no que você me contou...</h1>
      <p className="jota-onboard-text">Montei uma rotina pra você começar:</p>

      <div className="jota-onboard-routine-groups">
        <div className="jota-onboard-routine-group">
          <span className="jota-onboard-routine-label">Manhã</span>
          {morningPicks.map((p) => (
            <div key={p.id} className="jota-onboard-routine-item">
              <ProductThumb name={p.name} photo={p.photo} size={36} />
              <div className="jota-onboard-routine-item-text">
                <span className="title">{p.name}</span>
                <span className="sub">{p.stepType}</span>
              </div>
            </div>
          ))}
        </div>
        <div className="jota-onboard-routine-group">
          <span className="jota-onboard-routine-label">Noite</span>
          {nightPicks.map((p) => (
            <div key={p.id} className="jota-onboard-routine-item">
              <ProductThumb name={p.name} photo={p.photo} size={36} />
              <div className="jota-onboard-routine-item-text">
                <span className="title">{p.name}</span>
                <span className="sub">{p.stepType}</span>
              </div>
            </div>
          ))}
        </div>
      </div>

      <button
        className="jota-review-submit"
        onClick={() => {
          const toStep = (p) => ({
            id: crypto.randomUUID(),
            label: p.name,
            productId: p.id,
            photo: null,
            recurrence: { type: "diario" },
            completions: {},
          });
          onAccept([
            { id: crypto.randomUUID(), name: "Manhã", steps: morningPicks.map(toStep) },
            { id: crypto.randomUUID(), name: "Noite", steps: nightPicks.map(toStep) },
          ]);
        }}
      >
        Aceitar essa rotina
      </button>
      <button className="jota-onboard-secondary" onClick={onSkip}>Prefiro montar a minha</button>
    </div>
  );
}

// Tela de transição depois da análise: o J-OTA "pensando", com contador em
// ritmo orgânico (acelera, quase para, acelera de novo) e frases que comentam
// o que a pessoa preencheu — dá a sensação de que ele montou algo só pra ela.
// Ao concluir, o botão "Ver resultado!" leva pra rotina sugerida.
const THINK_WAYPOINTS = [
  { t: 0.00, p: 0 }, { t: 0.09, p: 18 }, { t: 0.27, p: 23 }, { t: 0.40, p: 51 },
  { t: 0.58, p: 56 }, { t: 0.73, p: 82 }, { t: 0.87, p: 88 }, { t: 1.00, p: 100 },
];
function AnalysisThinkingScreen({ name, profile, onDone }) {
  const [pct, setPct] = useState(0);
  const startRef = useRef(null);
  const rafRef = useRef(null);
  const done = pct >= 100;

  const phrases = useMemo(() => {
    const list = [`Deixa eu revisar tudo que você me contou${name ? ", " + name : ""}...`];
    if (profile?.skinType) list.push(`Pele ${String(profile.skinType).toLowerCase()} — anotado.`);
    const incomodo = profile?.priority || (profile?.concerns || [])[0];
    if (incomodo) list.push(`${incomodo} como principal incômodo. Vou priorizar isso.`);
    list.push("Buscando ativos que combinam com a sua pele...");
    list.push("Ajustando as concentrações. Quase pronto.");
    return list;
  }, [name, profile]);

  useEffect(() => {
    const RUN = 9000;
    const easeInOut = (x) => 0.5 - 0.5 * Math.cos(Math.PI * x);
    const progAt = (tt) => {
      for (let i = 1; i < THINK_WAYPOINTS.length; i++) {
        if (tt <= THINK_WAYPOINTS[i].t) {
          const a = THINK_WAYPOINTS[i - 1], b = THINK_WAYPOINTS[i];
          return a.p + (b.p - a.p) * easeInOut((tt - a.t) / (b.t - a.t));
        }
      }
      return 100;
    };
    const tick = (ts) => {
      if (startRef.current == null) startRef.current = ts;
      const t = Math.min(1, (ts - startRef.current) / RUN);
      setPct(Math.round(progAt(t)));
      if (t < 1) rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); };
  }, []);

  const phraseIdx = Math.min(phrases.length - 1, Math.floor(pct / (100 / phrases.length)));
  const thought = done ? "Personalizado para você." : phrases[phraseIdx];

  return (
    <div className="jota-onboard-screen jota-thinking-screen">
      <JotaOrb size={100} />
      <span className="jota-thinking-eyebrow">{done ? "concluído!" : "Só um instante"}</span>
      <h1 className="jota-thinking-title">
        {done ? "Prontinho!" : <>Personalizando<br />a sua experiência.</>}
      </h1>
      <div className="jota-thinking-prog">
        <span className="jota-thinking-pct">{pct}%</span>
        <div className="jota-thinking-bar"><div className="jota-thinking-bar-fill" style={{ width: pct + "%" }} /></div>
      </div>
      <div className="jota-thinking-thought">
        <span key={done ? "done" : phraseIdx} className="jota-thinking-phrase">{thought}</span>
      </div>
      <button
        className={`jota-review-submit jota-thinking-cta ${done ? "show" : ""}`}
        onClick={onDone}
      >
        Ver resultado!
      </button>
    </div>
  );
}

const TOUR_CARDS = [
  { icon: Check, title: "Rotina", desc: "Monte sua rotina personalizada com lembretes, frequência, calendário e muito mais." },
  { icon: Search, title: "Busca", desc: "Descubra produtos que combinam com sua pele, favorite, avalie e ganhe Atons." },
  { icon: BookOpen, title: "Diário", desc: "Acompanhe sua evolução com fotos, anotações, análises e consistência semanal." },
  { icon: null, title: "Converse com o J-OTA", desc: "Seu Assistente Inteligente de Beleza para tirar dúvidas sobre pele, skincare e dermocosméticos, personalizar rotinas e ajudar em tarefas dentro do app." },
];

function TourScreen({ onFinish }) {
  const [step, setStep] = useState(0);
  const card = TOUR_CARDS[step];
  const isLast = step === TOUR_CARDS.length - 1;
  const Icon = card.icon;

  return (
    <div className="jota-onboard-screen jota-tour-screen">
      <div className="jota-tour-icon">
        {Icon ? <Icon size={38} strokeWidth={1.5} /> : <JotaOrb size={60} />}
      </div>
      <h2 className="jota-onboard-title">{card.title}</h2>
      <p className="jota-onboard-text">{card.desc}</p>

      <div className="jota-rate-progress jota-tour-progress">
        {TOUR_CARDS.map((_, i) => (
          <span key={i} className={`jota-rate-progress-dot ${i === step ? "active" : ""} ${i < step ? "passed" : ""}`} />
        ))}
      </div>

      <button className="jota-review-submit" onClick={() => (isLast ? onFinish() : setStep((s) => s + 1))}>
        {isLast ? "Começar a usar" : "Próximo"}
      </button>
      {!isLast && (
        <button className="jota-onboard-secondary jota-tour-skip-bottom" onClick={onFinish}>Pular tutorial</button>
      )}
    </div>
  );
}

// Limite diário do J-OTA: data no fuso de Brasília (a contagem zera à meia-noite
// local) e o tempo restante até o próximo reset (00h), pro temporizador.
const brasilToday = () =>
  new Date().toLocaleDateString("en-CA", { timeZone: "America/Sao_Paulo" });

function msToBrasilMidnight() {
  const parts = new Intl.DateTimeFormat("en-GB", {
    timeZone: "America/Sao_Paulo", hour: "2-digit", minute: "2-digit",
    second: "2-digit", hourCycle: "h23",
  }).formatToParts(new Date());
  const get = (t) => parseInt(parts.find((p) => p.type === t).value, 10);
  const elapsed = get("hour") * 3600 + get("minute") * 60 + get("second");
  return (86400 - elapsed) * 1000;
}

function fmtCountdown(ms) {
  const total = Math.max(0, Math.floor(ms / 1000));
  const h = Math.floor(total / 3600);
  const m = Math.floor((total % 3600) / 60);
  return h > 0 ? `${h}h ${m}min` : `${m}min`;
}

export default function JotaApp() {
  const [tab, setTab] = useState("rotina");
  const [expanded, setExpanded] = useState(false);
  const [routineProductView, setRoutineProductView] = useState(null);
  const [myRatings, setMyRatings] = useState({});
  const [favorites, setFavorites] = useState({});
  const [wishlist, setWishlist] = useState({});
  const [necessaire, setNecessaire] = useState({});
  const [productStatus, setProductStatus] = useState({});
  const [necessaireOpen, setNecessaireOpen] = useState(false);
  const [testadosOpen, setTestadosOpen] = useState(false);

  const toggleFavorite = (id) => {
    const isFav = !!favorites[id];
    setFavorites((f) => ({ ...f, [id]: !isFav }));
    if (!session?.user) return;
    if (isFav) {
      persist(supabase.from("favorites").delete().eq("user_id", session.user.id).eq("product_id", id), "remover favorito");
    } else {
      persist(supabase.from("favorites").insert({ user_id: session.user.id, product_id: id }), "favoritar produto");
    }
  };

  const toggleWishlist = (id) => {
    const isIn = !!wishlist[id];
    setWishlist((w) => ({ ...w, [id]: !isIn }));
    if (!session?.user) return;
    if (isIn) {
      persist(supabase.from("wishlist").delete().eq("user_id", session.user.id).eq("product_id", id), "remover dos desejos");
    } else {
      persist(supabase.from("wishlist").insert({ user_id: session.user.id, product_id: id }), "adicionar aos desejos");
    }
  };

  const toggleNecessaire = (id) => {
    const isIn = !!necessaire[id];
    setNecessaire((n) => ({ ...n, [id]: !isIn }));
    if (!session?.user) return;
    if (isIn) {
      persist(supabase.from("necessaire").delete().eq("user_id", session.user.id).eq("product_id", id), "remover da necessaire");
    } else {
      persist(supabase.from("necessaire").insert({ user_id: session.user.id, product_id: id }), "adicionar à necessaire");
    }
  };

  // Marca um produto oficial na necessaire sem desmarcar quem já estava lá
  // (usado quando o produto entra numa rotina — nunca remove automaticamente).
  const markNecessaire = (id) => {
    if (!id || necessaire[id]) return;
    setNecessaire((n) => ({ ...n, [id]: true }));
    if (!session?.user) return;
    persist(
      supabase.from("necessaire").upsert(
        { user_id: session.user.id, product_id: id },
        { onConflict: "user_id,product_id", ignoreDuplicates: true }
      ),
      "marcar necessaire automaticamente"
    );
  };

  // Cicla o status de teste do produto: nenhum → testando → aprovado → reprovado → nenhum.
  const cycleProductStatus = (id) => {
    const order = [undefined, "testando", "aprovado", "reprovado"];
    const next = order[(order.indexOf(productStatus[id]) + 1) % order.length];
    setProductStatus((s) => {
      const copy = { ...s };
      if (next) copy[id] = next; else delete copy[id];
      return copy;
    });
    if (!session?.user) return;
    if (next) {
      persist(
        supabase.from("product_status").upsert(
          { user_id: session.user.id, product_id: id, status: next, updated_at: new Date().toISOString() },
          { onConflict: "user_id,product_id" }
        ),
        "atualizar status de teste"
      );
    } else {
      persist(supabase.from("product_status").delete().eq("user_id", session.user.id).eq("product_id", id), "remover status de teste");
    }
  };

  // Define o status de teste diretamente pra um valor específico (usado na
  // TestadosSheet, onde o controle é um segmentado de 3 opções, não um ciclo).
  const setProductStatusTo = (id, status) => {
    setProductStatus((s) => {
      const copy = { ...s };
      if (status) copy[id] = status; else delete copy[id];
      return copy;
    });
    if (!session?.user) return;
    if (status) {
      persist(
        supabase.from("product_status").upsert(
          { user_id: session.user.id, product_id: id, status, updated_at: new Date().toISOString() },
          { onConflict: "user_id,product_id" }
        ),
        "atualizar status de teste"
      );
    } else {
      persist(supabase.from("product_status").delete().eq("user_id", session.user.id).eq("product_id", id), "remover status de teste");
    }
  };

  // Monta as props de tag (Desejos/Necessaire/Testado) de um produto pra
  // passar direto pro ProductTagActions, em qualquer tela que mostre o produto.
  const tagsFor = (productId) => ({
    isWishlist: !!wishlist[productId],
    onToggleWishlist: toggleWishlist,
    isNecessaire: !!necessaire[productId],
    onToggleNecessaire: toggleNecessaire,
    status: productStatus[productId],
    onCycleStatus: cycleProductStatus,
  });

  const rateProduct = (productId, rating) => {
    setMyRatings((r) => ({ ...r, [productId]: rating }));
    if (!session?.user) return;
    persist(
      supabase.from("product_ratings").upsert(
        { user_id: session.user.id, product_id: productId, rating },
        { onConflict: "user_id,product_id" }
      ),
      "avaliar produto"
    );
  };

  // Publica a avaliação escrita (texto + nota + anônimo) na timeline do produto,
  // e mantém a nota numérica em product_ratings em sincronia. Retorna a promise
  // pro sheet recarregar a timeline só depois de salvar.
  const submitReview = async (productId, { rating, comment, anonymous }) => {
    setMyRatings((r) => ({ ...r, [productId]: rating }));
    if (!session?.user) return;
    const authorName = session.user.user_metadata?.name
      || (userEmail ? userEmail.split("@")[0] : null);
    try {
      const { error } = await supabase.from("product_reviews").upsert(
        {
          user_id: session.user.id,
          product_id: productId,
          rating,
          comment: (comment || "").trim() || null,
          anonymous: !!anonymous,
          author_name: authorName,
        },
        { onConflict: "user_id,product_id" }
      );
      if (error) throw error;
      await supabase.from("product_ratings").upsert(
        { user_id: session.user.id, product_id: productId, rating },
        { onConflict: "user_id,product_id" }
      );
    } catch (e) {
      console.error("[J-OTA] Falha ao publicar avaliação:", e.message || e);
    }
  };

  const [pointsHistory, setPointsHistory] = useState([]);
  const points = pointsHistory.reduce((sum, h) => sum + h.amount, 0);
  const addPoints = (amount, description) => {
    const id = crypto.randomUUID();
    const createdAt = new Date().toISOString();
    setPointsHistory((h) => [{ id, amount, description: description || "Atons ganhos", date: "Agora", createdAt }, ...h]);
    if (!session?.user) return;
    persist(
      supabase.from("points_history").insert({ id, user_id: session.user.id, amount, description: description || "Atons ganhos" }),
      "adicionar pontos"
    );
  };
  const [pointsSheetOpen, setPointsSheetOpen] = useState(false);
  const [notifications, setNotifications] = useState([]);
  const [notificationsOpen, setNotificationsOpen] = useState(false);

  const unreadCount = notifications.filter((n) => !n.read).length;

  const toggleNotificationRead = (id) => {
    const target = notifications.find((n) => n.id === id);
    setNotifications((ns) => ns.map((n) => n.id === id ? { ...n, read: !n.read } : n));
    if (!session?.user || !target) return;
    persist(supabase.from("notifications").update({ read: !target.read }).eq("id", id), "marcar notificação");
  };

  const markAllNotificationsRead = () => {
    setNotifications((ns) => ns.map((n) => ({ ...n, read: true })));
    if (!session?.user) return;
    // filtro explícito de usuário (defesa em profundidade além do RLS)
    persist(supabase.from("notifications").update({ read: true }).eq("user_id", session.user.id).eq("read", false), "marcar notificações");
  };
  const [routines, setRoutines] = useState([]);
  const [profile, setProfile] = useState(null);
  const [wizardOpen, setWizardOpen] = useState(false);
  const [onboardingStep, setOnboardingStep] = useState("login");
  const [session, setSession] = useState(null);
  // Recuperação de senha: quando a pessoa volta pelo link do e-mail, o Supabase
  // dispara PASSWORD_RECOVERY e abrimos a tela de definir nova senha por cima.
  const [recovering, setRecovering] = useState(false);
  const [messages, setMessages] = useState(initialMessages);
  const [apiMessages, setApiMessages] = useState([]);
  // Conversa persistida (tabela chat_conversations): null enquanto nada foi
  // trocado de verdade ainda — só ganha id no primeiro turno real.
  const [conversationId, setConversationId] = useState(null);
  const [chatHistoryOpen, setChatHistoryOpen] = useState(false);
  const [chatHistoryList, setChatHistoryList] = useState([]);
  const [chatGreeting, setChatGreeting] = useState(pickGreeting);
  const [input, setInput] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  // Limite diário de mensagens do J-OTA (fase 1: 5/dia). dailyCount vem do banco
  // (contador) e o servidor é a autoridade; nowTick faz o temporizador andar.
  const [dailyCount, setDailyCount] = useState(0);
  const [dailyLimit, setDailyLimit] = useState(5);
  const [nowTick, setNowTick] = useState(Date.now());
  const [voiceMode, setVoiceMode] = useState(false);
  const [chatDisclaimerOpen, setChatDisclaimerOpen] = useState(false);
  const [speakingId, setSpeakingId] = useState(null);
  // Teclado aberto (campo de texto em foco) → esconde a navbar pra não flutuar sobre o teclado.
  const [keyboardOpen, setKeyboardOpen] = useState(false);
  useEffect(() => {
    const isField = (el) => el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable);
    const onIn = (e) => { if (isField(e.target)) setKeyboardOpen(true); };
    const onOut = (e) => { if (isField(e.target)) setKeyboardOpen(false); };
    document.addEventListener("focusin", onIn);
    document.addEventListener("focusout", onOut);
    return () => { document.removeEventListener("focusin", onIn); document.removeEventListener("focusout", onOut); };
  }, []);
  // Sugestões do chat girando (janela de 3, avança sozinha na tela inicial).
  const [suggIdx, setSuggIdx] = useState(0);
  useEffect(() => {
    if (!expanded || messages.length > 0 || voiceMode) return;
    const id = setInterval(() => setSuggIdx((i) => (i + 4) % CHAT_SUGGESTIONS.length), 7000);
    return () => clearInterval(id);
  }, [expanded, messages.length, voiceMode]);
  const visibleSuggestions = [0, 1, 2, 3].map((k) => CHAT_SUGGESTIONS[(suggIdx + k) % CHAT_SUGGESTIONS.length]);

  const speakMessage = (text, idx) => {
    if (!window.speechSynthesis) return;
    window.speechSynthesis.cancel();
    const utter = new SpeechSynthesisUtterance(text);
    utter.lang = "pt-BR";
    utter.rate = 1.0;
    utter.onstart = () => setSpeakingId(idx);
    utter.onend = () => setSpeakingId(null);
    utter.onerror = () => setSpeakingId(null);
    window.speechSynthesis.speak(utter);
  };
  const scrollRef = useRef(null);

  // ── Ditado por voz (Web Speech API) ─────────────────────────────
  // Toca o mic → ouve em pt-BR, a transcrição vai preenchendo o input
  // ao vivo; quando a pessoa para de falar, envia sozinho pro J-OTA.
  // O X cancela sem enviar. sendRef aponta sempre pro handleSend mais
  // recente (o onend dispara depois e não pode usar closure velha).
  const recognitionRef = useRef(null);
  const voiceFinalRef = useRef("");
  const voiceCancelledRef = useRef(false);
  const sendRef = useRef(() => {});

  const stopVoice = () => {
    voiceCancelledRef.current = true;
    try { recognitionRef.current?.abort(); } catch (_) {}
    setVoiceMode(false);
    setInput("");
  };

  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) {
      setMessages((m) => [...m, {
        role: "assistant",
        content: "Ainda não consigo te ouvir neste navegador — o ditado por voz não é suportado aqui. Digita sua mensagem que eu respondo igualzinho! 🙂",
      }]);
      return;
    }
    const rec = new SR();
    rec.lang = "pt-BR";
    rec.interimResults = true;
    rec.continuous = false;
    voiceFinalRef.current = "";
    voiceCancelledRef.current = false;

    rec.onresult = (e) => {
      let text = "";
      for (let i = 0; i < e.results.length; i++) text += e.results[i][0].transcript;
      setInput(text);
      if (e.results[e.results.length - 1].isFinal) voiceFinalRef.current = text;
    };
    rec.onerror = (e) => {
      voiceCancelledRef.current = true;
      setVoiceMode(false);
      if (e.error === "not-allowed" || e.error === "service-not-allowed") {
        setMessages((m) => [...m, {
          role: "assistant",
          content: "Preciso da sua permissão pra usar o microfone 🎙️ — libera nas configurações do navegador e tenta de novo!",
        }]);
      }
    };
    rec.onend = () => {
      setVoiceMode(false);
      const finalText = (voiceFinalRef.current || "").trim();
      voiceFinalRef.current = "";
      if (!voiceCancelledRef.current && finalText) {
        sendRef.current(finalText);
      }
    };

    recognitionRef.current = rec;
    setInput("");
    setVoiceMode(true);
    try { rec.start(); } catch (_) { setVoiceMode(false); }
  };

  // Carrega rotinas + passos + conclusões do banco e monta o formato
  // aninhado que a UI já usa. RLS garante que só vêm os dados do usuário.
  const loadRoutines = async (userId) => {
    const { data: rts, error } = await supabase
      .from("routines")
      .select("id, name, order_index, created_at, hide_categories")
      .order("order_index", { ascending: true })
      .order("created_at", { ascending: true });
    if (error) {
      console.error("[J-OTA] load routines:", error.message);
      showToast("Não consegui carregar suas rotinas. Verifique a conexão.", () => loadRoutines(userId));
      return;
    }

    const routineIds = (rts || []).map((r) => r.id);
    let steps = [];
    if (routineIds.length) {
      const { data: st } = await supabase
        .from("routine_steps")
        .select("id, routine_id, label, product_id, photo_url, recurrence_type, recurrence_data, order_index, category")
        .in("routine_id", routineIds)
        .order("order_index", { ascending: true });
      steps = st || [];
    }

    const stepIds = steps.map((s) => s.id);
    let completions = [];
    if (stepIds.length) {
      const { data: cp } = await supabase
        .from("step_completions")
        .select("step_id, date_key")
        .in("step_id", stepIds);
      completions = cp || [];
    }
    const compByStep = {};
    completions.forEach((c) => { (compByStep[c.step_id] ||= {})[c.date_key] = true; });

    setRoutines((rts || []).map((r) => ({
      id: r.id,
      name: r.name,
      hideCategories: !!r.hide_categories,
      steps: steps
        .filter((s) => s.routine_id === r.id)
        .map((s) => ({
          id: s.id,
          label: s.label,
          productId: s.product_id,
          photo: s.photo_url,
          recurrence: joinRecurrence(s.recurrence_type, s.recurrence_data),
          completions: compByStep[s.id] || {},
          category: s.category || null,
        })),
    })));
  };

  // Executa a gravação no banco; se falhar, loga e recarrega do servidor
  // pra não deixar a tela divergir do banco silenciosamente.
  const persist = async (query, label) => {
    try {
      const { error } = await query;
      if (error) throw error;
    } catch (e) {
      console.error(`[J-OTA] Falha ao salvar (${label}):`, e.message || e);
      // Re-sincroniza com o servidor pra a UI otimista não divergir do banco em
      // caso de falha na escrita. Recarrega rotinas E dados do usuário (favoritos,
      // desejos, necessaire, status, pontos, notificações) — antes só as rotinas
      // eram re-sincronizadas, então falhas em coleções ficavam dessincronizadas.
      if (session?.user) {
        loadRoutines(session.user.id);
        loadUserData(session.user.id);
      }
    }
  };

  const toggleStep = (routineId, stepId, dateKey) => {
    const key = dateKey || todayKey();
    const step = routines.find((r) => r.id === routineId)?.steps.find((s) => s.id === stepId);
    const currently = !!step?.completions?.[key];

    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : {
      ...r,
      steps: r.steps.map((s) => s.id !== stepId ? s : {
        ...s,
        completions: { ...s.completions, [key]: !currently },
      }),
    }));

    if (!session?.user) return;
    if (currently) {
      persist(supabase.from("step_completions").delete().eq("step_id", stepId).eq("date_key", key), "desmarcar passo");
    } else {
      persist(supabase.from("step_completions").insert({ step_id: stepId, user_id: session.user.id, date_key: key }), "marcar passo");
    }
  };

  const addStep = (routineId, label, recurrence, photo, productId, category) => {
    const id = crypto.randomUUID();
    const rec = recurrence || { type: "diario" };
    const orderIndex = routines.find((r) => r.id === routineId)?.steps.length || 0;

    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : {
      ...r,
      steps: [...r.steps, { id, label, productId: productId || null, photo: photo || null, recurrence: rec, completions: {}, category: category || null }],
    }));

    // Produto oficial adicionado a uma rotina entra automaticamente na necessaire
    // (a pessoa já tem/usa esse produto). Nunca remove ao tirar da rotina.
    if (productId) markNecessaire(productId);

    if (!session?.user) return;
    const { recurrence_type, recurrence_data } = splitRecurrence(rec);
    persist(supabase.from("routine_steps").insert({
      id, routine_id: routineId, label,
      product_id: productId || null, photo_url: photo || null,
      recurrence_type, recurrence_data, order_index: orderIndex,
      category: category || null,
    }), "adicionar passo");
  };

  const removeStep = (routineId, stepId) => {
    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : {
      ...r, steps: r.steps.filter((s) => s.id !== stepId),
    }));
    if (!session?.user) return;
    persist(supabase.from("routine_steps").delete().eq("id", stepId), "remover passo");
  };

  const moveStep = (routineId, stepId, direction) => {
    const routine = routines.find((r) => r.id === routineId);
    if (!routine) return;
    const idx = routine.steps.findIndex((s) => s.id === stepId);
    const newIdx = idx + direction;
    if (newIdx < 0 || newIdx >= routine.steps.length) return;
    const other = routine.steps[newIdx];

    setRoutines((rs) => rs.map((r) => {
      if (r.id !== routineId) return r;
      const steps = [...r.steps];
      [steps[idx], steps[newIdx]] = [steps[newIdx], steps[idx]];
      return { ...r, steps };
    }));

    if (!session?.user) return;
    // Troca os order_index dos dois passos envolvidos.
    persist(supabase.from("routine_steps").update({ order_index: newIdx }).eq("id", stepId), "reordenar passo");
    persist(supabase.from("routine_steps").update({ order_index: idx }).eq("id", other.id), "reordenar passo");
  };

  const editRecurrence = (routineId, stepId, recurrence) => {
    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : {
      ...r, steps: r.steps.map((s) => s.id !== stepId ? s : { ...s, recurrence }),
    }));
    if (!session?.user) return;
    const { recurrence_type, recurrence_data } = splitRecurrence(recurrence);
    persist(supabase.from("routine_steps").update({ recurrence_type, recurrence_data }).eq("id", stepId), "editar recorrência");
  };

  // Renomear/trocar miniatura de um produto personalizado (lápis).
  const editStepInfo = (routineId, stepId, { label, photo }) => {
    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : {
      ...r, steps: r.steps.map((s) => s.id !== stepId ? s : { ...s, label, photo }),
    }));
    if (!session?.user) return;
    persist(supabase.from("routine_steps").update({ label, photo_url: photo }).eq("id", stepId), "editar produto personalizado");
  };

  const renameRoutine = (routineId, name) => {
    setRoutines((rs) => rs.map((r) => r.id !== routineId ? r : { ...r, name }));
    if (!session?.user) return;
    persist(supabase.from("routines").update({ name }).eq("id", routineId), "renomear rotina");
  };

  // Liga/desliga a exibição dos rótulos de categoria (LIMPEZA, etc.) nessa rotina.
  const toggleRoutineCategories = (routineId) => {
    let nextHidden = false;
    setRoutines((rs) => rs.map((r) => {
      if (r.id !== routineId) return r;
      nextHidden = !r.hideCategories;
      return { ...r, hideCategories: nextHidden };
    }));
    if (!session?.user) return;
    persist(supabase.from("routines").update({ hide_categories: nextHidden }).eq("id", routineId), "categorias da rotina");
  };

  const addRoutine = (name) => {
    const id = crypto.randomUUID();
    const orderIndex = routines.length;
    setRoutines((rs) => [...rs, { id, name, steps: [] }]);
    if (!session?.user) return;
    persist(supabase.from("routines").insert({ id, user_id: session.user.id, name, order_index: orderIndex }), "criar rotina");
  };

  const deleteRoutine = (routineId) => {
    setRoutines((rs) => rs.filter((r) => r.id !== routineId));
    if (!session?.user) return;
    persist(supabase.from("routines").delete().eq("id", routineId), "excluir rotina");
  };

  const moveRoutine = (routineId, direction) => {
    const idx = routines.findIndex((r) => r.id === routineId);
    const newIdx = idx + direction;
    if (idx < 0 || newIdx < 0 || newIdx >= routines.length) return;
    const other = routines[newIdx];

    setRoutines((rs) => {
      const arr = [...rs];
      [arr[idx], arr[newIdx]] = [arr[newIdx], arr[idx]];
      return arr;
    });

    if (!session?.user) return;
    // Troca os order_index das duas rotinas envolvidas.
    persist(supabase.from("routines").update({ order_index: newIdx }).eq("id", routineId), "reordenar rotina");
    persist(supabase.from("routines").update({ order_index: idx }).eq("id", other.id), "reordenar rotina");
  };

  // Persiste as rotinas sugeridas aceitas no onboarding (2 rotinas + passos).
  const acceptSuggestedRoutines = async (newRoutines) => {
    setRoutines(newRoutines);
    if (!session?.user) return;
    const routinesRows = newRoutines.map((r, i) => ({ id: r.id, user_id: session.user.id, name: r.name, order_index: i }));
    const stepsRows = newRoutines.flatMap((r) =>
      r.steps.map((s, i) => {
        const { recurrence_type, recurrence_data } = splitRecurrence(s.recurrence);
        return {
          id: s.id, routine_id: r.id, label: s.label,
          product_id: s.productId || null, photo_url: s.photo || null,
          recurrence_type, recurrence_data, order_index: i,
        };
      })
    );
    await persist(supabase.from("routines").insert(routinesRows), "criar rotinas sugeridas");
    await persist(supabase.from("routine_steps").insert(stepsRows), "criar passos sugeridos");
    stepsRows.filter((s) => s.product_id).forEach((s) => markNecessaire(s.product_id));
  };

  // Salva o resultado do questionário de análise no banco (upsert —
  // refazer a análise sobrescreve o perfil anterior).
  const saveSkinProfile = (result) => {
    setProfile(result);
    if (!session?.user) return;
    persist(
      supabase.from("skin_profiles").upsert({
        user_id: session.user.id,
        birthdate: result.birthdate || null,
        gender: result.gender || null,
        skin_type: result.skinType || null,
        concerns: result.concerns || [],
        priority: result.priority || null,
        body_regions: result.bodyRegions || [],
        goal: result.goal || null,
        products_used: result.productsUsed || [],
        frequency: result.frequency || null,
        duration: result.duration || null,
        reactivity: result.reactivity || null,
        risk_flags: result.riskFlags || [],
        routine_pref: result.routinePref || null,
        level: result.level || null,
        updated_at: new Date().toISOString(),
      }),
      "salvar perfil de pele"
    );
  };

  // Fecha o onboarding: vai pra Rotina e marca a conta como "onboarded" no
  // user_metadata, pra a experiência de primeira vez não reaparecer. Chamado ao
  // terminar a análise OU ao escolher "Fazer depois".
  const finishOnboarding = async () => {
    setOnboardingStep("done");
    try { await supabase.auth.updateUser({ data: { onboarded: true } }); }
    catch (e) { console.error("[J-OTA] marcar onboarded:", e.message || e); }
  };

  // Carrega favoritos, avaliações, perfil de pele, pontos e notificações.
  // Na primeira vez, semeia o bônus e a notificação de boas-vindas.
  const loadUserData = async (userId) => {
    const { data: favs } = await supabase.from("favorites").select("product_id");
    setFavorites(Object.fromEntries((favs || []).map((f) => [f.product_id, true])));

    const { data: wish } = await supabase.from("wishlist").select("product_id");
    setWishlist(Object.fromEntries((wish || []).map((w) => [w.product_id, true])));

    const { data: nec } = await supabase.from("necessaire").select("product_id");
    setNecessaire(Object.fromEntries((nec || []).map((n) => [n.product_id, true])));

    const { data: pstatus } = await supabase.from("product_status").select("product_id, status");
    setProductStatus(Object.fromEntries((pstatus || []).map((s) => [s.product_id, s.status])));

    const { data: ratings } = await supabase.from("product_ratings").select("product_id, rating");
    setMyRatings(Object.fromEntries((ratings || []).map((r) => [r.product_id, r.rating])));

    const { data: sp } = await supabase.from("skin_profiles").select("*").maybeSingle();
    if (sp) {
      setProfile({
        birthdate: sp.birthdate, gender: sp.gender,
        skinType: sp.skin_type, concerns: sp.concerns || [], priority: sp.priority,
        bodyRegions: sp.body_regions || [], goal: sp.goal, productsUsed: sp.products_used || [],
        frequency: sp.frequency, duration: sp.duration, reactivity: sp.reactivity,
        riskFlags: sp.risk_flags || [], routinePref: sp.routine_pref, level: sp.level,
      });
    }

    let { data: ph } = await supabase
      .from("points_history")
      .select("id, amount, description, created_at")
      .order("created_at", { ascending: false });
    if (!ph || ph.length === 0) {
      const bonus = { id: crypto.randomUUID(), user_id: userId, amount: 100, description: "Bônus de boas-vindas" };
      await supabase.from("points_history").insert(bonus);
      ph = [{ ...bonus, created_at: new Date().toISOString() }];
    }
    setPointsHistory(ph.map((p) => ({ id: p.id, amount: p.amount, description: p.description, date: formatRelativeDate(p.created_at), createdAt: p.created_at })));

    let { data: ns } = await supabase
      .from("notifications")
      .select("id, title, message, read, created_at")
      .order("created_at", { ascending: false });
    if (!ns || ns.length === 0) {
      const welcome = {
        id: crypto.randomUUID(),
        user_id: userId,
        title: "Boas-vindas ao J-OTA!",
        message: "Você ganhou 100 Atons de boas-vindas. Monte sua rotina e fale com o J-OTA quando quiser.",
      };
      await supabase.from("notifications").insert(welcome);
      ns = [{ ...welcome, read: false, created_at: new Date().toISOString() }];
    }
    setNotifications(ns.map((n) => ({ id: n.id, title: n.title, message: n.message, read: n.read, date: formatRelativeDate(n.created_at) })));
  };

  // Chat com o J-OTA: carrega a conversa mais recente do usuário (se tiver)
  // pra abrir o chat já mostrando de onde parou, como um app de conversa de verdade.
  const loadLatestChatConversation = async (userId) => {
    const { data } = await supabase
      .from("chat_conversations")
      .select("id, messages, api_messages")
      .order("updated_at", { ascending: false })
      .limit(1)
      .maybeSingle();
    if (data) {
      setMessages(data.messages?.length ? data.messages : initialMessages);
      setApiMessages(data.api_messages || []);
      setConversationId(data.id);
    }
  };

  // Salva a conversa depois de cada turno completo (pergunta + resposta).
  // Sem conversationId ainda = primeiro turno real, cria a linha; senão atualiza.
  const saveConversation = async (nextMessages, nextApiMessages, firstUserText) => {
    if (!session?.user) return;
    try {
      if (conversationId) {
        const { error } = await supabase
          .from("chat_conversations")
          .update({ messages: nextMessages, api_messages: nextApiMessages, updated_at: new Date().toISOString() })
          .eq("id", conversationId);
        if (error) throw error;
      } else {
        const title = (firstUserText || "Conversa").slice(0, 60);
        const { data, error } = await supabase
          .from("chat_conversations")
          .insert({ user_id: session.user.id, title, messages: nextMessages, api_messages: nextApiMessages })
          .select("id")
          .single();
        if (error) throw error;
        setConversationId(data.id);
      }
    } catch (e) {
      console.error("[J-OTA] Falha ao salvar conversa:", e.message || e);
    }
  };

  const startNewConversation = () => {
    setMessages(initialMessages);
    setApiMessages([]);
    setConversationId(null);
    setChatHistoryOpen(false);
    setChatGreeting(pickGreeting());
  };

  const openChatHistory = async () => {
    setChatHistoryOpen(true);
    if (!session?.user) return;
    const { data, error } = await supabase
      .from("chat_conversations")
      .select("id, title, updated_at")
      .order("updated_at", { ascending: false });
    if (error) console.error("[J-OTA] Falha ao carregar conversas:", error.message);
    setChatHistoryList(data || []);
  };

  const openConversation = async (id) => {
    const { data } = await supabase
      .from("chat_conversations")
      .select("id, messages, api_messages")
      .eq("id", id)
      .single();
    if (data) {
      setMessages(data.messages?.length ? data.messages : initialMessages);
      setApiMessages(data.api_messages || []);
      setConversationId(data.id);
    }
    setChatHistoryOpen(false);
  };

  // Catálogo vem do banco (tabela products). O PRODUCT_CATALOG embutido é
  // só o estado inicial — trocado in-place pelo do servidor após o login,
  // então cadastrar produto no banco aparece no app sem mexer em código.
  const [, setCatalogVersion] = useState(0);
  const loadCatalog = async () => {
    const { data: prods, error } = await supabase.from("products").select("*").order("id");
    if (error || !prods?.length) return;
    const mapped = prods.map((p) => ({
      id: p.id, name: p.name, brand: p.brand,
      concerns: p.concerns || [], skinTypes: p.skin_types || [],
      rating: Number(p.rating) || 0, ratingCount: p.rating_count || 0,
      period: p.period, stepType: p.step_type, bodyArea: p.body_area,
      size: Number(p.size) || 0, sizeUnit: p.size_unit,
      priceMin: Number(p.price_min) || 0, priceMax: Number(p.price_max) || 0,
      description: p.description, howToUse: p.how_to_use,
      keyIngredients: p.key_ingredients || [], contraindications: p.contraindications,
      actives: p.actives || [], buyLink: p.buy_link || "#",
      photo: p.image_url || null,   // foto do produto (coluna image_url)
    }));
    PRODUCT_CATALOG.splice(0, PRODUCT_CATALOG.length, ...mapped);
    setCatalogVersion((v) => v + 1); // força re-render com o catálogo novo
  };

  // Remove o splash de marca (definido no index.html) só DEPOIS que a sessão
  // foi checada E (se tiver usuário logado) as rotinas já carregaram do banco.
  // Antes disso o splash ficava só por um tempo fixo, então dava pra ver a
  // tela de rotina "zerada" por um instante até o loadRoutines terminar.
  const [sessionChecked, setSessionChecked] = useState(false);
  const [routinesLoaded, setRoutinesLoaded] = useState(false);
  const appDataReady = sessionChecked && (session?.user ? routinesLoaded : true);

  // Toast global de erro (ex.: falha de conexão ao carregar dados). { message, retry } | null.
  const [toast, setToast] = useState(null);
  const toastTimer = useRef(null);
  const showToast = (message, retry = null) => {
    setToast({ message, retry });
    clearTimeout(toastTimer.current);
    // com botão de retry, fica até a pessoa agir; sem retry, some sozinho.
    if (!retry) toastTimer.current = setTimeout(() => setToast(null), 4500);
  };

  useEffect(() => {
    if (!appDataReady) return;
    const t = setTimeout(() => {
      const splash = document.getElementById("jota-splash");
      if (splash) {
        splash.classList.add("hide");
        setTimeout(() => splash.remove(), 400);
      }
    }, 60);
    return () => clearTimeout(t);
  }, [appDataReady]);

  // Sessão de login: recupera a sessão salva (o supabase-js já persiste
  // no localStorage) e escuta mudanças. Primeira vez (conta ainda não
  // "onboarded" no metadata) → mostra o onboarding; senão vai direto pro app.
  useEffect(() => {
    let active = true;
    supabase.auth.getSession().then(({ data }) => {
      if (!active) return;
      setSession(data.session);
      // Primeira vez (metadata sem a flag onboarded) → onboarding; senão, app.
      // Numa sessão restaurada a flag já vem certa pra quem logou depois de
      // tê-la; no login fresco (onAuthed), confirmamos no servidor via getUser.
      if (data.session) setOnboardingStep(data.session.user?.user_metadata?.onboarded ? "done" : "welcome");
      setSessionChecked(true);
    });
    const { data: sub } = supabase.auth.onAuthStateChange((_event, newSession) => {
      setSession(newSession);
      if (_event === "PASSWORD_RECOVERY") setRecovering(true);
      if (!newSession) {
        // Logout: volta pra tela de login e limpa dados locais da conta.
        setOnboardingStep("login");
        setRoutines([]);
        setProfile(null);
        setFavorites({});
        setMyRatings({});
        setPointsHistory([]);
        setNotifications([]);
        setMessages(initialMessages);
        setApiMessages([]);
        setConversationId(null);
        setTab("rotina");
      }
    });
    return () => {
      active = false;
      sub.subscription.unsubscribe();
    };
  }, []);

  const logout = async () => {
    await supabase.auth.signOut();
  };

  // Atualiza o nome no user_metadata do Supabase Auth. O onAuthStateChange já
  // escutado acima recebe o evento USER_UPDATED e atualiza a sessão sozinho.
  const updateName = async (name) => {
    const { error } = await supabase.auth.updateUser({ data: { name: name.trim() || null } });
    if (error) console.error("[J-OTA] atualizar nome:", error.message);
  };

  // Ao logar (ou restaurar sessão), carrega as rotinas do banco.
  useEffect(() => {
    setRoutinesLoaded(false);
    if (session?.user) {
      loadRoutines(session.user.id).then(() => setRoutinesLoaded(true));
      loadUserData(session.user.id);
      loadLatestChatConversation(session.user.id);
      loadCatalog();
    } else {
      setRoutines([]);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [session?.user?.id]);

  // Primeira vez sem nenhuma rotina: cria "Dia" e "Noite" como ponto de
  // partida (vazias — o "+ Adicionar produto" fica visível no card).
  // O flag no localStorage evita recriar se o usuário apagar tudo de propósito.
  useEffect(() => {
    if (!routinesLoaded || onboardingStep !== "done" || !session?.user) return;
    if (routines.length > 0) return;
    const flagKey = `jota-starter-${session.user.id}`;
    if (localStorage.getItem(flagKey)) return;
    localStorage.setItem(flagKey, "1");

    const starters = [
      { id: crypto.randomUUID(), name: "Manhã", steps: [] },
      { id: crypto.randomUUID(), name: "Noite", steps: [] },
    ];
    setRoutines(starters);
    persist(
      supabase.from("routines").insert(
        starters.map((r, i) => ({ id: r.id, user_id: session.user.id, name: r.name, order_index: i }))
      ),
      "criar rotinas iniciais"
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [routinesLoaded, onboardingStep, routines.length, session?.user?.id]);

  const userEmail = session?.user?.email || null;
  const userName = session?.user?.user_metadata?.name
    || (userEmail ? userEmail.split("@")[0] : "você");
  const firstName = capitalize((userName || "você").split(" ")[0]);

  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [messages, isLoading]);

  // Contagem de mensagens de hoje (pro contador). Recarrega ao abrir o chat.
  // RLS garante que só vem a linha do próprio usuário.
  useEffect(() => {
    if (!session?.user) { setDailyCount(0); return; }
    let cancelled = false;
    (async () => {
      const { data } = await supabase
        .from("chat_usage").select("count").eq("day", brasilToday()).maybeSingle();
      if (!cancelled) setDailyCount(data?.count ?? 0);
    })();
    return () => { cancelled = true; };
  }, [session?.user?.id, expanded]);

  // Enquanto está no limite e o chat aberto, o temporizador anda de 1 em 1s.
  useEffect(() => {
    if (!expanded || dailyCount < dailyLimit) return;
    const id = setInterval(() => setNowTick(Date.now()), 1000);
    return () => clearInterval(id);
  }, [expanded, dailyCount, dailyLimit]);

  async function handleSend(overrideText) {
    // overrideText: usado pelo ditado por voz e pelas opções clicáveis (string);
    // cliques no botão de enviar passam evento, por isso o typeof abaixo.
    const text = (typeof overrideText === "string" ? overrideText : input).trim();
    if (!text || isLoading) return;
    if (dailyCount >= dailyLimit) return; // no limite: o banner substitui o input

    const messagesAfterUser = [...messages, { role: "user", content: text }];
    setMessages(messagesAfterUser);
    setInput("");
    setIsLoading(true);

    // Formato da Anthropic: mantém o histórico da conversa entre turnos.
    const history = [...apiMessages, { role: "user", content: text }];

    try {
      // Chama a Edge Function segura em vez de bater direto na Anthropic.
      // A chave da IA fica no servidor; as tools (add_product_to_routine,
      // create_routine, open_screen) rodam lá, escopadas ao usuário logado.
      // O JWT do usuário vai junto automaticamente (cliente autenticado).
      const { data, error } = await supabase.functions.invoke("jota-chat", {
        body: { history },
      });

      if (error) {
        let detail = error.message;
        try { detail = JSON.stringify(await error.context.json()); } catch (_) {}
        throw new Error(detail);
      }

      // Servidor barrou por limite: desfaz a bolha otimista e mostra o banner.
      if (data?.limitReached) {
        setDailyCount(data.count ?? dailyLimit);
        setMessages(messages);
        return;
      }

      const nextApiMessages = data.history || history;
      const nextMessages = [
        ...messagesAfterUser,
        { role: "assistant", content: data.reply || "Prontinho!", actions: data.actions || [] },
      ];
      setApiMessages(nextApiMessages);
      setMessages(nextMessages);
      if (typeof data.count === "number") setDailyCount(data.count);
      saveConversation(nextMessages, nextApiMessages, text);

      // As ações do J-OTA são executadas no servidor (escrevem no banco).
      // Recarrega as rotinas pra refletir na tela e navega se ele pediu.
      if (data.actions?.length) {
        if (session?.user) loadRoutines(session.user.id);
        const nav = data.actions
          .map((a) => (a.match(/Abrindo a tela (rotina|busca|diario)/) || [])[1])
          .find(Boolean);
        if (nav) setTab(nav);
      }
    } catch (err) {
      console.error("[J-OTA] chat:", err.message || err);
      const nextMessages = [
        ...messagesAfterUser,
        { role: "assistant", content: "Perdi a conexão por um instante — tenta de novo?" },
      ];
      setMessages(nextMessages);
      saveConversation(nextMessages, apiMessages, text);
    } finally {
      setIsLoading(false);
    }
  }
  sendRef.current = handleSend;

  return (
    <div className="jota-app">
      {toast && (
        <div className="jota-toast" role="status" aria-live="polite">
          <span className="jota-toast-msg">{toast.message}</span>
          {toast.retry && (
            <button className="jota-toast-action" onClick={() => { const r = toast.retry; setToast(null); r(); }}>
              Tentar de novo
            </button>
          )}
          <button className="jota-toast-close" onClick={() => setToast(null)} aria-label="Fechar aviso">
            <X size={14} strokeWidth={2.4} />
          </button>
        </div>
      )}
      {recovering && (
        <div className="jota-onboard-overlay">
          <ResetPasswordScreen onDone={() => setRecovering(false)} />
        </div>
      )}
      <style>{`
        @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap');

        .jota-app {
          --ink: #16161A;
          --ink-soft: #4A4A52;
          --muted: #6E6E76;
          --hairline: rgba(20,20,24,0.09);
          --hairline-strong: rgba(20,20,24,0.14);
          --paper: #FFFFFF;
          --fog: #FAFAFA;
          --coral: #2A2A2E;
          --coral-soft: #6E6E76;
          font-family: 'Open Sans', sans-serif;
          font-weight: 400;
          background: var(--paper);
          color: var(--ink);
          width: 100%;
          max-width: 420px;
          height: 800px;
          margin: 0 auto;
          border-radius: 32px;
          overflow: hidden;
          position: relative;
          box-shadow: 0 24px 70px rgba(20,20,24,0.16);
          border: 1px solid var(--hairline);
        }


        /* Toast global de erro (ex.: sem conexão) — barra sóbria escura com vidro,
           no padrão do CTA/preto do app. Fica acima da navbar, some sozinho (ou
           espera a pessoa clicar em "Tentar de novo" quando há retry). */
        .jota-toast {
          position: fixed;
          left: 50%;
          bottom: calc(96px + env(safe-area-inset-bottom, 0px));
          transform: translateX(-50%);
          z-index: 50;
          max-width: min(420px, calc(100% - 32px));
          width: max-content;
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 11px 11px 11px 16px;
          background: rgba(22,22,26,0.93);
          -webkit-backdrop-filter: blur(12px) saturate(160%);
          backdrop-filter: blur(12px) saturate(160%);
          border: 1px solid rgba(255,255,255,0.10);
          border-radius: 16px;
          box-shadow: 0 12px 34px rgba(20,20,24,0.28);
          animation: jota-toast-in 0.24s cubic-bezier(0.22, 1, 0.36, 1);
        }
        @keyframes jota-toast-in {
          from { opacity: 0; transform: translate(-50%, 8px); }
          to { opacity: 1; transform: translate(-50%, 0); }
        }
        .jota-toast-msg {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          line-height: 1.35;
          color: #fff;
        }
        .jota-toast-action {
          flex-shrink: 0;
          background: rgba(255,255,255,0.16);
          border: none;
          border-radius: 100px;
          padding: 7px 13px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 700;
          color: #fff;
          cursor: pointer;
        }
        .jota-toast-close {
          flex-shrink: 0;
          background: none;
          border: none;
          color: rgba(255,255,255,0.55);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          padding: 2px;
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-toast { animation: none; }
        }
        .jota-header {
          position: absolute;
          /* ancorado no topo absoluto: o vidro cobre até a área do notch (safe-area),
             sem tarja branca acima; o conteúdo desce via padding-top. */
          top: 0;
          left: 0;
          right: 0;
          z-index: 7;
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: calc(20px + env(safe-area-inset-top, 0px)) 20px 18px;
          /* vidro: fundo translúcido + desfoque do que rola por trás */
          background: rgba(255,255,255,0.5);
          -webkit-backdrop-filter: blur(22px) saturate(180%);
          backdrop-filter: blur(22px) saturate(180%);
          /* sem linha cinza: fio branco sutil + sombra só na base. */
          border-bottom: 1px solid rgba(255,255,255,0.55);
          box-shadow: 0 8px 24px rgba(40,45,70,0.07);
          /* cantos inferiores arredondados (voltados pro conteúdo) */
          border-bottom-left-radius: 26px;
          border-bottom-right-radius: 26px;
        }
        .jota-header-right {
          display: flex;
          align-items: center;
          gap: 6px;
        }
        .jota-header-profile-btn {
          display: flex;
          align-items: center;
          gap: 6px;
          background: none;
          border: none;
          padding: 0;
          cursor: pointer;
        }
        .jota-header-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink);
        }
        .jota-header-avatar {
          width: 26px;
          height: 26px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--muted);
        }
        .jota-notif-btn {
          position: relative;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          color: var(--ink-soft);
          width: 28px;
          height: 28px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-notif-badge {
          position: absolute;
          top: -2px;
          right: -4px;
          min-width: 15px;
          height: 15px;
          padding: 0 3px;
          border-radius: 100px;
          background: var(--ink);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 9.5px;
          font-weight: 700;
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-points-badge {
          display: flex;
          align-items: center;
          gap: 5px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 6px 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-logo-img {
          height: 40px;
          width: auto;
          display: block;
          transform: translateY(2px);
        }

        .jota-body {
          height: 100%;
          overflow-y: auto;
          -webkit-overflow-scrolling: touch;
          /* contém o scroll aqui dentro — sem isso, ao chegar no topo/base o
             gesto "vaza" pro body da página (bounce), que é quem fazia o app
             inteiro (header, navbar, orbe) parecer que "saía do lugar" */
          overscroll-behavior: contain;
          /* topo/base folgam pro conteúdo rolar por trás do header e da navbar de vidro */
          padding: 102px 20px calc(78px + 28px + env(safe-area-inset-bottom, 0px));
          box-sizing: border-box;
        }

        .jota-eyebrow {
          font-family: 'Open Sans', sans-serif;
          font-weight: 600;
          font-size: 10.5px;
          letter-spacing: 0.12em;
          text-transform: uppercase;
          color: var(--muted);
        }

        .jota-sky-banner {
          position: relative;
          height: 80px;
          border-radius: 18px;
          overflow: hidden;
          margin-bottom: 16px;
          display: flex;
          align-items: center;
          padding: 0 18px;
          isolation: isolate;
          /* vidro: borda superior iluminada + anel interno + sombra de profundidade */
          box-shadow: inset 0 1px 1px rgba(255,255,255,0.5),
                      inset 0 0 0 1px rgba(255,255,255,0.14),
                      inset 0 -10px 22px rgba(0,0,0,0.07);
        }
        /* REFLEXO de vidro: brilho suave no topo (não cobre a hora, centralizada) */
        .jota-sky-banner::after {
          content: '';
          position: absolute;
          top: 0; left: 0; right: 0;
          height: 46%;
          z-index: 4;
          pointer-events: none;
          background: linear-gradient(180deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.06) 42%, transparent 100%);
        }
        /* FUNDOS por período — paleta da orbe J-OTA (rosa/roxo/azul/ciano/menta/âmbar) */
        .jota-sky-banner.madrugada { background: linear-gradient(115deg, #12102A 0%, #1E1440 55%, #2A1652 100%); }
        .jota-sky-banner.amanhecer { background: linear-gradient(100deg, #F7B4D0 0%, #D3A9E6 36%, #7FA8E8 70%, #4FBFDE 100%); }
        .jota-sky-banner.manha     { background: linear-gradient(100deg, #8FCDEE 0%, #5FB9E6 48%, #4CD3C0 100%); }
        .jota-sky-banner.tarde     { background: linear-gradient(100deg, #6FB8E8 0%, #86C7DE 42%, #B4D9BE 72%, #F6C85A 100%); }
        .jota-sky-banner.fimtarde  { background: linear-gradient(100deg, #FFAF5C 0%, #FF7DA3 38%, #C356E0 68%, #6A3FBE 100%); }
        .jota-sky-banner.noite     { background: linear-gradient(100deg, #6E48C6 0%, #4C2F94 55%, #241653 100%); }

        /* HAZE — manchas radiais suaves (profundidade, sem nuvem/estrela desenhada) */
        .jota-sky-haze { position: absolute; inset: 0; z-index: 1; mix-blend-mode: screen; opacity: 0.55; pointer-events: none; }
        .jota-sky-banner.madrugada .jota-sky-haze { background: radial-gradient(120px 90px at 14% 30%, rgba(160,90,255,0.55), transparent 70%), radial-gradient(140px 100px at 92% 80%, rgba(35,215,235,0.22), transparent 70%); }
        .jota-sky-banner.amanhecer .jota-sky-haze { background: radial-gradient(160px 110px at 20% 60%, rgba(255,75,190,0.28), transparent 70%), radial-gradient(150px 100px at 88% 20%, rgba(80,240,190,0.25), transparent 70%); }
        .jota-sky-banner.manha .jota-sky-haze { background: radial-gradient(150px 110px at 18% 30%, rgba(255,255,255,0.4), transparent 70%), radial-gradient(150px 100px at 90% 85%, rgba(80,240,190,0.35), transparent 70%); }
        .jota-sky-banner.tarde .jota-sky-haze { background: radial-gradient(160px 110px at 16% 30%, rgba(80,240,190,0.3), transparent 70%), radial-gradient(150px 110px at 92% 80%, rgba(255,170,85,0.35), transparent 70%); }
        .jota-sky-banner.fimtarde .jota-sky-haze { background: radial-gradient(160px 110px at 18% 65%, rgba(255,75,190,0.35), transparent 70%), radial-gradient(160px 110px at 90% 20%, rgba(160,90,255,0.3), transparent 70%); }
        .jota-sky-banner.noite .jota-sky-haze { background: radial-gradient(150px 110px at 82% 25%, rgba(35,215,235,0.28), transparent 70%), radial-gradient(150px 110px at 16% 80%, rgba(255,75,190,0.2), transparent 70%); }

        /* LOOP "sail": elemento atravessa (-25%→125%) e reentra pelo outro lado, fora da tela (sem emenda) */
        @keyframes jota-sky-sail { from { left: var(--sail-from, -25%); } to { left: var(--sail-to, 125%); } }
        .jota-sky-banner .sail { animation: jota-sky-sail var(--sail-dur, 60s) linear infinite; animation-delay: var(--sail-delay, 0s); will-change: left; }

        /* SOL — disco simples, amarelo por estágio */
        .jota-sky-orb { position: relative; z-index: 2; margin-left: 2px; flex-shrink: 0; border-radius: 9999px; }
        .jota-sky-orb.sun { width: 25px; height: 25px; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.3); }
        .jota-sky-banner.amanhecer .jota-sky-orb.sun { background: radial-gradient(circle at 35% 30%, #FFFBEB 0%, #FFE38A 45%, #FFCB57 100%); }
        .jota-sky-banner.manha .jota-sky-orb.sun { background: radial-gradient(circle at 35% 30%, #FFFFF2 0%, #FFEE7A 42%, #FFD93D 100%); }
        .jota-sky-banner.tarde .jota-sky-orb.sun { background: radial-gradient(circle at 35% 30%, #FFF6DA 0%, #FFD35C 45%, #FFB238 100%); }
        .jota-sky-banner.fimtarde .jota-sky-orb.sun { background: radial-gradient(circle at 35% 30%, #FFEDC2 0%, #FFC24D 45%, #FFA224 100%); }
        /* LUA — branca com degradê interno, manchas suaves e brilho atrás */
        .jota-sky-orb.moon { width: 23px; height: 23px; background: radial-gradient(circle at 36% 32%, #FFFFFF 0%, #F4F3F9 32%, #E3E1EB 62%, #C9C7D6 100%); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.3); }
        .jota-sky-orb.moon::before { content: ''; position: absolute; inset: -70%; border-radius: 9999px; background: radial-gradient(circle, var(--moon-glow) 0%, transparent 68%); filter: blur(6px); opacity: 0.4; z-index: -1; }
        .jota-sky-orb.moon::after { content: ''; position: absolute; inset: 0; border-radius: 9999px; background: radial-gradient(circle at 30% 62%, rgba(110,110,130,0.26) 0%, transparent 78%), radial-gradient(circle at 62% 40%, rgba(110,110,130,0.18) 0%, transparent 78%), radial-gradient(circle at 68% 68%, rgba(110,110,130,0.14) 0%, transparent 78%); filter: blur(1.6px); }
        .jota-sky-banner.madrugada .jota-sky-orb.moon { --moon-glow: rgba(160,150,255,0.7); }
        .jota-sky-banner.noite .jota-sky-orb.moon { --moon-glow: rgba(185,147,245,0.75); }

        /* ESTRELAS orgânicas (posição sorteada via JS ao apagar) */
        .jota-sky-star { position: absolute; z-index: 1; border-radius: 9999px; background: #FFFFFF; box-shadow: 0 0 4px 1px rgba(255,255,255,0.55); animation: jota-sky-twinkle 7s ease-in-out infinite; }
        .jota-sky-banner.amanhecer .jota-sky-star { background: #FFF7FB; box-shadow: 0 0 3px 1px rgba(255,255,255,0.8), 0 0 0 1px rgba(150,105,175,0.4); }

        /* NUVENS — vários puffs (radial-gradients), borda macia */
        .jota-sky-cloud { position: absolute; z-index: 1; width: 42px; height: 16px; --cloud-op: 0.85; background: radial-gradient(ellipse 30% 60% at 24% 66%, rgba(255,255,255,var(--cloud-op)) 55%, transparent 78%), radial-gradient(ellipse 26% 78% at 44% 44%, rgba(255,255,255,var(--cloud-op)) 55%, transparent 78%), radial-gradient(ellipse 30% 66% at 64% 54%, rgba(255,255,255,var(--cloud-op)) 55%, transparent 78%), radial-gradient(ellipse 24% 50% at 84% 70%, rgba(255,255,255,var(--cloud-op)) 55%, transparent 78%), radial-gradient(ellipse 60% 40% at 50% 88%, rgba(255,255,255,var(--cloud-op)) 55%, transparent 82%); background-repeat: no-repeat; filter: blur(0.5px); }
        .jota-sky-cloud.sm { width: 28px; height: 11px; --cloud-op: 0.7; }
        .jota-sky-cloud.wispy { --cloud-op: 0.5; filter: blur(1.4px); }

        /* PÁSSAROS (gaivotas) */
        .jota-sky-bird { position: absolute; z-index: 2; color: rgba(60,44,70,0.55); }
        .jota-sky-bird svg { display: block; width: 100%; height: 100%; overflow: visible; }
        .jota-sky-wing { transform-box: fill-box; transform-origin: center bottom; animation: jota-sky-flap 1.6s ease-in-out infinite; }
        .jota-sky-bird.b2 .jota-sky-wing { animation-duration: 1.9s; animation-delay: -0.4s; }

        /* TEXTO (data + hora), sempre branco com sombra sutil pra ler em qualquer fundo */
        .jota-sky-text { position: absolute; z-index: 3; right: 18px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
        .jota-sky-weekday { font-family: 'Open Sans', sans-serif; font-weight: 600; font-size: 13px; color: rgba(255,255,255,0.78); text-shadow: 0 1px 6px rgba(0,0,0,0.22); }
        .jota-sky-clock { font-family: 'Open Sans', sans-serif; font-weight: 800; font-size: 23px; letter-spacing: 0.01em; color: #FFFFFF; text-shadow: 0 1px 8px rgba(0,0,0,0.28); }

        @keyframes jota-sky-twinkle { 0%, 100% { opacity: 0; transform: scale(0.5); } 50% { opacity: 1; transform: scale(1.12); } }
        @keyframes jota-sky-flap { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(0.45); } }
        @media (prefers-reduced-motion: reduce) { .jota-sky-banner .sail, .jota-sky-star, .jota-sky-wing { animation: none; } .jota-sky-star { opacity: 0.75; } }
        .jota-sky-pattern {
          position: absolute;
          inset: 0;
          overflow: hidden;
        }
        .jota-cloud {
          position: absolute;
          background: rgba(255,255,255,0.85);
          border-radius: 50px;
          filter: blur(0.3px);
        }
        .jota-cloud::before,
        .jota-cloud::after {
          content: '';
          position: absolute;
          background: inherit;
          border-radius: 50%;
        }
        .jota-cloud::before {
          width: 55%;
          height: 150%;
          top: -75%;
          left: 10%;
        }
        .jota-cloud::after {
          width: 65%;
          height: 125%;
          top: -58%;
          right: 8%;
        }
        .jota-cloud-1 { width: 44px; height: 13px; top: 13px; left: 7%; animation: jota-cloud-1-drift 14s ease-in-out infinite; }
        .jota-cloud-2 { width: 30px; height: 10px; top: 32px; left: 42%; opacity: 0.8; animation: jota-cloud-2-drift 10s ease-in-out infinite; }
        .jota-cloud-3 { width: 24px; height: 9px; top: 10px; left: 76%; opacity: 0.6; animation: jota-cloud-3-drift 17s ease-in-out infinite; }
        @keyframes jota-cloud-1-drift {
          0% { transform: translate(0, 0); opacity: 0.85; }
          22% { transform: translate(5px, -3px); opacity: 1; }
          48% { transform: translate(11px, 1px); opacity: 0.9; }
          71% { transform: translate(6px, 3px); opacity: 0.95; }
          100% { transform: translate(0, 0); opacity: 0.85; }
        }
        @keyframes jota-cloud-2-drift {
          0% { transform: translate(0, 0); }
          30% { transform: translate(-7px, 2px); }
          58% { transform: translate(-3px, -4px); }
          82% { transform: translate(-9px, 1px); }
          100% { transform: translate(0, 0); }
        }
        @keyframes jota-cloud-3-drift {
          0% { transform: translate(0, 0); opacity: 0.6; }
          26% { transform: translate(6px, 2px); opacity: 0.5; }
          63% { transform: translate(2px, -3px); opacity: 0.65; }
          88% { transform: translate(8px, 1px); opacity: 0.55; }
          100% { transform: translate(0, 0); opacity: 0.6; }
        }
        .jota-star {
          position: absolute;
          background: #FFFFFF;
          border-radius: 50%;
          opacity: 0.85;
          animation-name: jota-star-twinkle;
          animation-timing-function: ease-in-out;
          animation-iteration-count: infinite;
        }
        @keyframes jota-star-twinkle {
          0% { opacity: 0.35; transform: scale(0.88); }
          35% { opacity: 0.95; transform: scale(1.08); }
          55% { opacity: 0.4; transform: scale(0.9); }
          80% { opacity: 0.8; transform: scale(1.04); }
          100% { opacity: 0.35; transform: scale(0.88); }
        }
        .jota-sky-content {
          position: relative;
          z-index: 2;
          display: flex;
          align-items: center;
          gap: 10px;
          width: 100%;
          justify-content: space-between;
        }
        .jota-sky-icon {
          display: flex;
          align-items: center;
          justify-content: center;
          flex-shrink: 0;
          margin-left: 6px;
          position: relative;
        }
        .jota-sky-banner.day .jota-sky-icon {
          color: var(--ink-soft);
        }
        .jota-sky-banner.night .jota-sky-icon {
          color: #FFFFFF;
        }
        .jota-sky-icon.is-sun {
          animation: jota-sun-spin 50s linear infinite;
        }
        .jota-sky-icon.is-sun::before {
          content: '';
          position: absolute;
          inset: -10px;
          border-radius: 50%;
          background: radial-gradient(circle, rgba(20,20,24,0.12), transparent 70%);
          animation: jota-sun-glow 4.2s ease-in-out infinite;
          z-index: -1;
        }
        @keyframes jota-sun-spin {
          from { transform: rotate(0deg); }
          to { transform: rotate(360deg); }
        }
        @keyframes jota-sun-glow {
          0% { opacity: 0.45; transform: scale(0.95); }
          38% { opacity: 1; transform: scale(1.18); }
          65% { opacity: 0.7; transform: scale(1.02); }
          100% { opacity: 0.45; transform: scale(0.95); }
        }
        .jota-sky-icon.is-moon::before {
          content: '';
          position: absolute;
          inset: -8px;
          border-radius: 50%;
          background: radial-gradient(circle, rgba(255,255,255,0.18), transparent 70%);
          animation: jota-moon-glow 5.6s ease-in-out infinite;
          z-index: -1;
        }
        @keyframes jota-moon-glow {
          0% { opacity: 0.35; }
          42% { opacity: 0.9; }
          70% { opacity: 0.55; }
          100% { opacity: 0.35; }
        }
        /* (estilos do texto do céu movidos pro bloco novo do banner, acima) */

        .jota-h1 {
          font-family: 'Open Sans', sans-serif;
          font-size: 25px;
          font-weight: 700;
          margin: 5px 0 2px;
          letter-spacing: -0.01em;
        }
        .jota-sub {
          color: var(--muted);
          font-size: 13.5px;
          margin-bottom: 22px;
          line-height: 1.4;
        }

        .jota-card {
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 18px 18px 6px;
          margin-bottom: 14px;
        }
        .jota-reminder-card {
          display: flex;
          align-items: center;
          gap: 12px;
          padding: 16px;
          /* cinza, igual aos botões de Necessaire/Testados */
          background: var(--fog);
        }
        .jota-reminder-icon {
          width: 34px;
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--ink-soft);
          flex-shrink: 0;
        }
        .jota-reminder-text {
          flex: 1;
          display: flex;
          flex-direction: column;
          gap: 2px;
          min-width: 0;
        }
        .jota-reminder-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-reminder-sub {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 500;
          color: var(--muted);
        }
        .jota-reminder-check {
          width: 36px;
          height: 36px;
          border-radius: 50%;
          background: var(--paper);
          border: 1.5px solid var(--hairline-strong);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
        }
        .jota-reminder-check:active {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        @media (hover: hover) {
          .jota-reminder-check:hover {
            background: var(--ink);
            border-color: var(--ink);
            color: #fff;
          }
        }
        .jota-reminder-done .jota-reminder-icon {
          color: var(--ink);
        }
        .jota-card-head {
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 8px;
        }
        .jota-card-head-left {
          display: flex;
          align-items: center;
          gap: 9px;
          min-width: 0;
        }
        .jota-card-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 14.5px;
          font-weight: 600;
          display: flex;
          align-items: center;
          gap: 7px;
          color: var(--ink);
        }
        .jota-card-title-editable {
          cursor: pointer;
        }
        .jota-rename-icon {
          color: var(--muted);
        }
        .jota-rename-input {
          font-family: 'Open Sans', sans-serif;
          font-size: 14.5px;
          font-weight: 600;
          color: var(--ink);
          border: none;
          border-bottom: 1.5px solid var(--ink);
          background: transparent;
          padding: 0 0 2px;
          outline: none;
          max-width: 140px;
        }
        .jota-card-head-right {
          display: flex;
          align-items: center;
          gap: 10px;
        }
        .jota-card-count {
          font-family: 'Open Sans', sans-serif;
          font-weight: 500;
          font-size: 11.5px;
          color: var(--muted);
        }
        .jota-icon-btn {
          background: none;
          border: none;
          color: var(--muted);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          padding: 2px;
        }
        .jota-icon-btn:hover {
          color: var(--ink-soft);
        }
        .jota-check-all-btn {
          width: 22px;
          height: 22px;
          border-radius: 50%;
          background: var(--paper);
          border: 1.5px solid var(--hairline-strong);
          color: transparent;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
          transition: all 0.15s ease;
        }
        .jota-check-all-btn.checked {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-item {
          display: flex;
          align-items: center;
          gap: 12px;
          padding: 11px 0;
          border-top: 1px solid var(--hairline);
          cursor: pointer;
          background: var(--paper);
          touch-action: pan-y;
          transition: opacity 0.15s ease;
        }
        .jota-item.dragging {
          opacity: 0.5;
        }
        .jota-item:first-child { border-top: none; }
        .jota-swipe-wrap {
          position: relative;
          overflow: hidden;
        }
        .jota-swipe-actions {
          position: absolute;
          top: 0;
          right: 0;
          bottom: 0;
          width: 116px;
          display: flex;
          align-items: stretch;
        }
        .jota-swipe-action {
          flex: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          background: var(--fog);
          border: none;
          border-left: 1px solid var(--hairline);
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-swipe-action:active {
          background: var(--hairline);
        }
        .jota-drag-handle {
          background: none;
          border: none;
          color: var(--muted);
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 4px;
          margin-left: -4px;
          cursor: grab;
          touch-action: none;
          flex-shrink: 0;
        }
        .jota-drag-handle:active {
          cursor: grabbing;
          color: var(--ink-soft);
        }
        .jota-checkbox {
          width: 19px;
          height: 19px;
          border-radius: 50%;
          border: 1.5px solid var(--hairline-strong);
          flex-shrink: 0;
          display: flex;
          align-items: center;
          justify-content: center;
          transition: all 0.2s ease;
        }
        .jota-checkbox.checked {
          background: var(--ink);
          border-color: var(--ink);
        }
        .jota-item-label-col {
          flex: 1;
          display: flex;
          flex-direction: column;
          gap: 1px;
          cursor: pointer;
          min-width: 0;
        }
        .jota-item-label {
          font-size: 14px;
          color: var(--ink-soft);
          transition: color 0.2s ease, opacity 0.2s ease;
        }
        .jota-item-label.checked {
          color: var(--muted);
          text-decoration: line-through;
        }
        .jota-recur-badge {
          font-family: 'Open Sans', sans-serif;
          font-size: 9px;
          font-weight: 500;
          color: var(--muted);
          letter-spacing: 0.02em;
        }
        .jota-icon-btn.active {
          color: var(--ink);
        }
        .jota-empty-steps {
          font-size: 12.5px;
          color: var(--muted);
          padding: 10px 0;
        }
        .jota-step-block {
          border-top: 1px solid var(--hairline);
        }
        .jota-step-block:first-child {
          border-top: none;
        }
        /* No modo de edição: sem linhas dividindo os produtos, só os ícones espaçados */
        .jota-card.editing .jota-step-block {
          border-top: none;
        }
        .jota-card.editing .jota-step-block .jota-item {
          padding-top: 6px;
          padding-bottom: 6px;
        }
        .jota-step-block .jota-item {
          border-top: none;
        }
        .jota-add-block {
          border-top: 1px solid var(--hairline);
          padding: 12px 0;
        }
        .jota-add-block .jota-add-row {
          border-top: none;
          padding: 0 0 10px;
        }
        .jota-recur {
          margin-top: 8px;
          padding: 12px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 14px;
        }
        .jota-recur-chips {
          display: grid;
          grid-template-columns: 1fr 1fr;
          gap: 6px;
        }
        .jota-recur-chip {
          background: var(--paper);
          border: 1px solid var(--hairline-strong);
          border-radius: 10px;
          padding: 9px 8px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 500;
          color: var(--ink-soft);
          cursor: pointer;
          text-align: center;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        .jota-recur-chip.active {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-recur-subpanel {
          margin-top: 12px;
          padding-top: 12px;
          border-top: 1px solid var(--hairline-strong);
        }
        .jota-weekday-row {
          display: flex;
          justify-content: space-between;
        }
        .jota-weekday-dot {
          width: 30px;
          height: 30px;
          border-radius: 50%;
          border: 1px solid var(--hairline-strong);
          background: var(--paper);
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          cursor: pointer;
          flex-shrink: 0;
        }
        .jota-weekday-dot.active {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-interval-row {
          display: flex;
          align-items: center;
          gap: 8px;
          font-size: 13px;
          color: var(--ink-soft);
        }
        .jota-interval-input {
          width: 48px;
          text-align: center;
          border: 1px solid var(--hairline-strong);
          border-radius: 8px;
          padding: 6px 4px;
          background: var(--paper);
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          color: var(--ink);
        }
        .jota-dates-block {
        }
        .jota-dates-list {
          display: flex;
          flex-wrap: wrap;
          gap: 6px;
          margin-bottom: 8px;
        }
        .jota-date-chip {
          display: flex;
          align-items: center;
          gap: 5px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 5px 6px 5px 11px;
          font-size: 11.5px;
          color: var(--ink-soft);
        }
        .jota-date-chip button {
          background: none;
          border: none;
          color: var(--muted);
          display: flex;
          cursor: pointer;
          padding: 2px;
        }
        .jota-date-add-row {
          display: flex;
          align-items: center;
          gap: 8px;
        }
        .jota-date-input {
          border: 1px solid var(--hairline-strong);
          border-radius: 8px;
          padding: 6px 8px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          color: var(--ink);
          background: var(--paper);
        }
        .jota-item-actions {
          display: flex;
          align-items: center;
          gap: 2px;
          flex-shrink: 0;
        }
        .jota-item-action {
          background: none;
          border: none;
          color: var(--muted);
          width: 22px;
          height: 22px;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          border-radius: 6px;
        }
        .jota-item-action:hover:not(:disabled) {
          background: var(--fog);
          color: var(--ink-soft);
        }
        .jota-item-action:disabled {
          opacity: 0.25;
          cursor: default;
        }
        .jota-add-row {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 8px;
          padding: 11px 0;
          border-top: 1px solid var(--hairline);
        }
        .jota-add-input {
          flex: 1;
          border: none;
          background: none;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          color: var(--ink);
          outline: none;
          padding: 4px 0;
        }
        .jota-add-confirm {
          width: 26px;
          height: 26px;
          border-radius: 50%;
          background: var(--ink);
          color: #fff;
          border: none;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
        }
        .jota-add-step-btn {
          display: flex;
          align-items: center;
          gap: 6px;
          width: 100%;
          background: none;
          border: none;
          border-top: 1px solid var(--hairline);
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 500;
          padding: 12px 0;
          cursor: pointer;
          text-align: left;
        }
        .jota-add-step-btn:hover {
          color: var(--ink-soft);
        }
        .jota-icon-btn:disabled {
          opacity: 0.3;
          cursor: default;
        }
        .jota-routine-footer {
          display: flex;
          gap: 8px;
          margin-top: 4px;
          padding-top: 12px;
          border-top: 1px solid var(--hairline);
        }
        .jota-routine-delete-btn,
        .jota-routine-save-btn {
          flex: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          border-radius: 100px;
          padding: 11px 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          cursor: pointer;
        }
        .jota-routine-delete-btn {
          background: none;
          border: 1px solid #F0C9C9;
          color: #A32020;
        }
        .jota-routine-save-btn {
          background: var(--ink);
          border: 1px solid var(--ink);
          color: #fff;
        }
        /* Editor de recorrência (rascunho) + rodapé Cancelar/Aplicar */
        .jota-recur-edit {
          margin-top: 2px;
        }
        /* Pop-up de escopo da edição de recorrência (#18) */
        .jota-scope-overlay {
          position: absolute;
          inset: 0;
          background: rgba(20,20,24,0.45);
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 28px;
          z-index: 24;
        }
        .jota-scope-card {
          background: var(--paper);
          border-radius: 20px;
          padding: 22px 20px 16px;
          width: 100%;
          box-sizing: border-box;
          display: flex;
          flex-direction: column;
          box-shadow: 0 20px 60px rgba(20,20,24,0.22);
        }
        .jota-scope-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 16px;
          font-weight: 700;
          color: var(--ink);
          text-align: center;
        }
        .jota-scope-sub {
          font-size: 12.5px;
          line-height: 1.5;
          color: var(--ink-soft);
          text-align: center;
          margin: 6px 0 16px;
        }
        .jota-scope-options {
          display: flex;
          flex-direction: column;
          gap: 8px;
        }
        .jota-scope-opt {
          display: flex;
          flex-direction: column;
          gap: 3px;
          text-align: left;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 12px 14px;
          cursor: pointer;
          font-family: 'Open Sans', sans-serif;
          transition: border-color 0.12s, background 0.12s;
        }
        .jota-scope-opt:hover {
          border-color: var(--hairline-strong);
          background: #fff;
        }
        .jota-scope-opt-title {
          font-size: 13.5px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-scope-opt-desc {
          font-size: 11.5px;
          line-height: 1.45;
          color: var(--muted);
        }
        .jota-scope-cancel {
          margin-top: 12px;
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          cursor: pointer;
          padding: 6px 0 2px;
        }
        .jota-product-thumb {
          flex-shrink: 0;
          border-radius: 8px;
          object-fit: contain;
          background: #fff;
        }
        .jota-product-thumb-placeholder {
          display: flex;
          align-items: center;
          justify-content: center;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-weight: 700;
        }
        .jota-product-picker {
          padding-top: 4px;
        }
        .jota-product-search {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 11px 16px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          color: var(--ink);
          outline: none;
          margin-bottom: 8px;
        }
        .jota-product-search::placeholder { color: var(--muted); }
        /* Botão "Ver" (abre a página do produto) — discreto, à direita do item */
        .jota-item-view {
          flex-shrink: 0;
          background: none;
          border: none;
          color: var(--muted);
          width: 26px;
          height: 26px;
          display: flex;
          align-items: center;
          justify-content: center;
          border-radius: 8px;
          cursor: pointer;
        }
        .jota-item-view:hover {
          background: var(--fog);
          color: var(--ink-soft);
        }
        .jota-product-list {
          max-height: 220px;
          overflow-y: auto;
          margin-bottom: 8px;
        }
        .jota-product-option {
          display: flex;
          align-items: center;
          gap: 10px;
          width: 100%;
          background: none;
          border: none;
          border-top: 1px solid var(--hairline);
          padding: 9px 2px;
          cursor: pointer;
          text-align: left;
        }
        .jota-product-option:first-child {
          border-top: none;
        }
        .jota-product-option:hover {
          background: var(--fog);
        }
        .jota-product-option-text {
          display: flex;
          flex-direction: column;
          gap: 1px;
          min-width: 0;
        }
        .jota-product-option-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 500;
          color: var(--ink);
        }
        .jota-product-option-brand {
          display: flex;
          align-items: center;
          gap: 6px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
        }
        .jota-product-rating {
          display: inline-flex;
          align-items: center;
          gap: 2px;
          color: var(--ink-soft);
          font-weight: 600;
        }
        .jota-product-concerns {
          font-family: 'Open Sans', sans-serif;
          font-size: 10px;
          color: var(--muted);
          opacity: 0.8;
        }
        /* Opção "Adicionar manualmente" — fica no TOPO da lista, como CTA */
        .jota-product-custom {
          border-top: none;
          color: var(--ink);
          font-size: 13px;
          font-weight: 700;
          gap: 8px;
          background: var(--fog);
          border-radius: 10px;
          padding: 11px 12px;
          margin-bottom: 6px;
        }
        .jota-product-custom-plus {
          display: inline-flex;
          align-items: center;
          justify-content: center;
          width: 22px;
          height: 22px;
          border-radius: 50%;
          background: var(--ink);
          color: #fff;
          flex-shrink: 0;
        }
        /* Criação de produto personalizado (nome + miniatura) */
        .jota-custom-product {
          padding: 4px 0 0;
        }
        .jota-custom-label {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--muted);
          margin: 12px 0 6px;
        }
        .jota-custom-name-input {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 12px 16px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          color: var(--ink);
          outline: none;
        }
        .jota-custom-counter {
          display: block;
          text-align: right;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          color: var(--muted);
          margin-top: 4px;
        }
        .jota-category-picker {
          margin-bottom: 4px;
        }
        .jota-category-select {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 11px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink);
          outline: none;
        }
        .jota-category-tag {
          display: inline-block;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 6px 13px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-step-category-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 8.5px;
          font-weight: 700;
          letter-spacing: 0.08em;
          text-transform: uppercase;
          color: var(--muted);
          text-align: left;
          padding: 12px 0 4px;
        }
        .jota-step-category-label:first-child {
          padding-top: 2px;
        }
        .jota-custom-icon-grid {
          display: grid;
          grid-template-columns: repeat(5, 1fr);
          gap: 8px;
        }
        .jota-custom-icon-option {
          aspect-ratio: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          background: var(--fog);
          border: 1.5px solid var(--hairline);
          border-radius: 12px;
          color: var(--ink-soft);
          cursor: pointer;
          transition: transform 0.12s ease, border-color 0.12s ease;
        }
        .jota-custom-icon-option:active {
          transform: scale(0.94);
        }
        .jota-custom-icon-option.selected {
          border-color: var(--ink);
          background: var(--paper);
          color: var(--ink);
        }
        .jota-add-cancel {
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          cursor: pointer;
          padding: 6px 4px;
        }
        .jota-add-cancel:hover {
          color: var(--ink-soft);
        }
        /* Rodapé do painel de adicionar produto (Cancelar + Adicionar) */
        .jota-add-footer {
          display: flex;
          align-items: center;
          gap: 8px;
          margin-top: 12px;
        }
        .jota-add-cancel-btn {
          flex: 1;
          background: none;
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 11px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-add-save-btn:disabled {
          opacity: 0.45;
          cursor: default;
        }
        .jota-add-save-btn {
          flex: 2;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          background: var(--ink);
          border: 1px solid var(--ink);
          border-radius: 100px;
          padding: 11px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          color: #fff;
          cursor: pointer;
        }
        /* Campo de horário no editor de recorrência */
        .jota-recur-time {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 10px;
          margin-top: 10px;
          padding-top: 10px;
          border-top: 1px solid var(--hairline);
        }
        .jota-recur-time-left {
          display: flex;
          align-items: center;
          gap: 6px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-recur-time-opt {
          font-weight: 400;
          color: var(--muted);
        }
        .jota-recur-time-right {
          display: flex;
          align-items: center;
          gap: 6px;
        }
        .jota-recur-time-input {
          border: 1px solid var(--hairline-strong);
          border-radius: 10px;
          padding: 7px 10px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          color: var(--ink);
          background: var(--paper);
          outline: none;
        }
        .jota-recur-time-clear {
          width: 24px;
          height: 24px;
          border-radius: 50%;
          border: 1px solid var(--hairline-strong);
          background: var(--paper);
          color: var(--muted);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
        }
        /* Meta (horário + recorrência) na linha do passo */
        .jota-item-meta {
          display: flex;
          align-items: center;
          flex-wrap: wrap;
          gap: 6px;
          margin-top: 2px;
        }
        /* Sutil, no mesmo tom do selo de recorrência — só com o reloginho */
        .jota-time-badge {
          display: inline-flex;
          align-items: center;
          gap: 3px;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 9px;
          font-weight: 500;
          letter-spacing: 0.02em;
        }
        .jota-product-chosen {
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 6px 2px 12px;
        }
        .jota-product-chosen-name {
          flex: 1;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink);
        }
        .jota-product-swap {
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-add-routine-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          width: 100%;
          background: none;
          border: 1px dashed var(--hairline-strong);
          border-radius: 20px;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          padding: 14px 0;
          cursor: pointer;
          margin-bottom: 14px;
        }
        .jota-add-routine-btn:hover {
          color: var(--ink-soft);
          border-color: var(--ink-soft);
        }
        .jota-add-routine-card {
          padding: 16px 18px;
        }
        .jota-preset-row {
          display: flex;
          align-items: center;
          gap: 8px;
          margin-top: 10px;
        }
        .jota-preset-chip {
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 6px 13px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 500;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-preset-chip:hover {
          background: var(--hairline);
        }
        .jota-featured-banner {
          position: relative;
          overflow: hidden;
          isolation: isolate;
          width: 100%;
          display: flex;
          align-items: center;
          gap: 14px;
          /* preto com degradê pra cinza escuro */
          background: linear-gradient(135deg, #131316 0%, #3A3A42 100%);
          border: none;
          border-radius: 18px;
          padding: 16px 18px;
          margin-bottom: 10px;
          text-align: left;
          cursor: pointer;
          /* vidro: borda superior iluminada + anel interno + profundidade */
          box-shadow: inset 0 1px 1px rgba(255,255,255,0.18),
                      inset 0 0 0 1px rgba(255,255,255,0.08),
                      inset 0 -10px 22px rgba(0,0,0,0.22);
        }
        /* reflexo de vidro: brilho suave no topo */
        .jota-featured-banner::after {
          content: '';
          position: absolute;
          top: 0; left: 0; right: 0;
          height: 46%;
          z-index: 0;
          pointer-events: none;
          background: linear-gradient(180deg, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.04) 42%, transparent 100%);
        }
        .jota-featured-banner > * { position: relative; z-index: 1; }
        .jota-featured-text {
          flex: 1;
          display: flex;
          flex-direction: column;
          gap: 2px;
          min-width: 0;
        }
        .jota-featured-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 15px;
          font-weight: 700;
          color: #fff;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
        /* textos claros sobre o banner preto */
        .jota-featured-banner .jota-promo-label { color: rgba(255,255,255,0.7); }
        .jota-featured-banner .jota-promo-sub { color: rgba(255,255,255,0.82); }
        .jota-featured-banner .jota-featured-dot { background: rgba(255,255,255,0.32); }
        .jota-featured-banner .jota-featured-dot.active { background: #fff; }
        .jota-featured-dots {
          display: flex;
          flex-direction: column;
          gap: 5px;
          align-self: center;
        }
        .jota-featured-dot {
          width: 5px;
          height: 5px;
          border-radius: 50%;
          background: var(--hairline-strong);
        }
        .jota-featured-dot.active {
          background: var(--ink);
        }
        .jota-promo-pair {
          display: flex;
          gap: 10px;
          margin-bottom: 14px;
        }
        .jota-promo-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 9.5px;
          font-weight: 700;
          letter-spacing: 0.05em;
          text-transform: uppercase;
          color: var(--muted);
        }
        .jota-promo-sub {
          display: flex;
          align-items: center;
          gap: 3px;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-collection-row {
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          gap: 10px;
          margin-bottom: 14px;
        }
        /* Se sobrar um chip ímpar na última linha, ele ocupa a linha toda (sem buraco) */
        .jota-collection-row > .jota-collection-chip:last-child:nth-child(odd) {
          grid-column: 1 / -1;
        }
        .jota-collection-chip {
          display: flex;
          align-items: center;
          gap: 12px;
          min-width: 0;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 16px;
          padding: 14px 14px;
          cursor: pointer;
          text-align: left;
          color: var(--ink-soft);
        }
        .jota-collection-chip-icon {
          display: flex;
          align-items: center;
          justify-content: center;
          width: 30px;
          color: var(--ink-soft);
          flex-shrink: 0;
        }
        .jota-collection-chip-text {
          display: flex;
          flex-direction: column;
          gap: 2px;
          min-width: 0;
        }
        .jota-collection-chip-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          color: var(--ink);
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
        /* label que pode quebrar em 2 linhas (rótulos longos, ex.: Rotina da Semana) */
        .jota-collection-chip-label.wrap {
          white-space: normal;
          overflow: visible;
          line-height: 1.25;
        }
        .jota-collection-chip-count {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          color: var(--muted);
        }
        .jota-status-badge {
          display: inline-block;
          font-family: 'Open Sans', sans-serif;
          font-size: 9px;
          font-weight: 700;
          letter-spacing: 0.02em;
          padding: 1px 7px;
          border-radius: 100px;
          margin-top: 2px;
        }
        .jota-status-badge.status-testando {
          background: rgba(199,126,26,0.14);
          color: #C77E1A;
        }
        .jota-status-badge.status-aprovado {
          background: rgba(46,125,79,0.14);
          color: #2E7D4F;
        }
        .jota-status-badge.status-reprovado {
          background: rgba(192,57,43,0.14);
          color: #C0392B;
        }
        /* Botão "Adicionar produto" na necessaire */
        .jota-necessaire-add-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          width: 100%;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 12px 14px;
          margin-bottom: 18px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-necessaire-add-plus {
          margin-left: auto;
          color: var(--ink);
          display: flex;
        }
        .jota-necessaire-add-empty {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          color: var(--muted);
          padding: 10px 2px;
        }
        /* Seção: produtos da rotina que saíram da necessaire (cinza, toque pra readicionar) */
        .jota-necessaire-removed {
          margin-top: 6px;
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 12px 12px 14px;
        }
        .jota-necessaire-removed-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--muted);
          margin-bottom: 6px;
          line-height: 1.35;
        }
        /* Só a miniatura e o nome ficam apagados; o + fica preto e vivo por cima */
        .jota-necessaire-item-ghost .jota-product-thumb { opacity: 0.5; }
        .jota-necessaire-item-ghost .jota-necessaire-item-name { color: var(--muted); }
        .jota-necessaire-item-add {
          position: absolute;
          top: -6px;
          right: 2px;
          width: 24px;
          height: 24px;
          border-radius: 50%;
          background: var(--ink);
          color: #fff;
          border: 2px solid var(--paper);
          box-shadow: 0 2px 7px rgba(20,20,24,0.32);
          display: flex;
          align-items: center;
          justify-content: center;
          z-index: 3;
        }
        .jota-necessaire-compartment {
          margin-bottom: 18px;
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 12px 12px 14px;
        }
        .jota-necessaire-compartment-head {
          display: flex;
          align-items: center;
          gap: 6px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 700;
          text-transform: uppercase;
          letter-spacing: 0.04em;
          color: var(--ink-soft);
          margin-bottom: 10px;
        }
        .jota-necessaire-compartment-count {
          background: var(--fog);
          border-radius: 100px;
          padding: 1px 7px;
          font-size: 10px;
        }
        .jota-necessaire-row {
          display: flex;
          gap: 12px;
          overflow-x: auto;
          /* folga no topo pra o badge (X / +) não ser cortado pelo overflow da linha */
          padding: 10px 2px 2px;
        }
        .jota-necessaire-item {
          position: relative;
          flex: 0 0 auto;
          width: 76px;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 4px;
          background: none;
          border: none;
          cursor: pointer;
          text-align: center;
        }
        .jota-necessaire-item-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
          color: var(--ink);
          line-height: 1.25;
        }
        .jota-necessaire-item-remove {
          position: absolute;
          top: -6px;
          right: 2px;
          width: 22px;
          height: 22px;
          border-radius: 50%;
          background: var(--paper);
          border: 1px solid var(--hairline-strong);
          box-shadow: 0 1px 5px rgba(20,20,24,0.16);
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--ink-soft);
          z-index: 3;
        }
        /* Topo da Testados: produtos ainda sem avaliação, com 3 botões redondos */
        .jota-testados-eval {
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 6px 14px 14px;
          margin-bottom: 18px;
        }
        .jota-testados-eval-row {
          padding: 18px 0 8px;
          border-top: 1px solid var(--hairline);
        }
        .jota-testados-eval-row:first-child {
          border-top: none;
        }
        .jota-testados-eval-info {
          display: flex;
          align-items: center;
          gap: 10px;
          width: 100%;
          background: none;
          border: none;
          padding: 0;
          cursor: pointer;
          text-align: left;
          margin-bottom: 10px;
        }
        .jota-testados-eval-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink);
          line-height: 1.3;
        }
        .jota-testados-choices {
          display: flex;
          gap: 8px;
        }
        .jota-testados-choice {
          flex: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 5px;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 9px 6px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-testados-choice:active {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-search-box {
          display: flex;
          align-items: center;
          gap: 10px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 13px 16px;
          margin-bottom: 14px;
        }
        .jota-search-input {
          flex: 1;
          border: none;
          background: none;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          color: var(--ink);
          outline: none;
        }
        .jota-search-input::placeholder {
          color: var(--muted);
        }
        /* Banner do Diário — nível de cuidado, mesma identidade do banner de rotina (céu) */
        .jota-diario-banner {
          position: relative;
          border-radius: 18px;
          overflow: hidden;
          isolation: isolate;
          display: flex;
          align-items: center;
          gap: 14px;
          padding: 10px 18px;
          margin-bottom: 16px;
          transition: background 0.6s ease;
          /* vidro: borda superior iluminada + anel interno + sombra de profundidade */
          box-shadow: inset 0 1px 1px rgba(255,255,255,0.5),
                      inset 0 0 0 1px rgba(255,255,255,0.14),
                      inset 0 -10px 22px rgba(0,0,0,0.07);
        }
        /* Reflexo de vidro: brilho suave no topo */
        .jota-diario-banner::after {
          content: '';
          position: absolute;
          top: 0; left: 0; right: 0;
          height: 46%;
          z-index: 4;
          pointer-events: none;
          background: linear-gradient(180deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.06) 42%, transparent 100%);
        }
        .jota-diario-banner-haze {
          position: absolute;
          inset: 0;
          z-index: 1;
          mix-blend-mode: screen;
          opacity: 0.6;
          pointer-events: none;
          background:
            radial-gradient(150px 110px at 18% 34%, rgba(255,255,255,0.28), transparent 70%),
            radial-gradient(150px 110px at 86% 82%, rgba(80,240,190,0.3), transparent 70%);
        }
        .jota-diario-banner-seed {
          position: relative;
          z-index: 3;
          flex-shrink: 0;
          width: 46px;
          height: 46px;
          display: grid;
          place-items: center;
          color: #fff;
          animation: jota-diario-breathe 4s ease-in-out infinite;
        }
        .jota-diario-banner-halo {
          position: absolute;
          inset: -30%;
          border-radius: 50%;
          filter: blur(7px);
          transition: opacity 0.6s ease, background 0.6s ease;
        }
        .jota-diario-banner-seed svg {
          position: relative;
          z-index: 2;
          filter: drop-shadow(0 1px 6px rgba(0,0,0,0.35));
        }
        @keyframes jota-diario-breathe { 0%, 100% { transform: scale(1); opacity: 0.9; } 50% { transform: scale(1.06); opacity: 1; } }
        .jota-diario-banner-info {
          position: relative;
          z-index: 3;
          margin-left: auto;
          text-align: right;
          width: 62%;
        }
        .jota-diario-banner-cap {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: rgba(255,255,255,0.72);
          margin-bottom: 2px;
        }
        .jota-diario-banner-lvl {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 21px;
          font-weight: 800;
          color: #fff;
          text-shadow: 0 1px 10px rgba(0,0,0,0.32);
          margin-bottom: 5px;
        }
        /* trilho = degradê do tema (dim); preenchido = mesmo degradê espelhado (contraste) */
        .jota-diario-banner-bar {
          position: relative;
          height: 7px;
          border-radius: 99px;
          overflow: hidden;
          margin-left: auto;
          width: 100%;
          background: rgba(255,255,255,0.14);
        }
        .jota-diario-banner-bar::before {
          content: '';
          position: absolute;
          inset: 0;
          background: linear-gradient(90deg, var(--barA, #50F0BE), var(--barB, #23D7EB));
          opacity: 0.32;
        }
        .jota-diario-banner-bar > i {
          position: relative;
          display: block;
          height: 100%;
          border-radius: 99px;
          background: linear-gradient(90deg, var(--barB, #23D7EB), var(--barA, #50F0BE));
          box-shadow: 0 0 8px 0 rgba(255,255,255,0.3);
          transition: width 0.6s ease, background 0.6s ease;
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-diario-banner-seed { animation: none; }
        }
        .jota-skin-cta {
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 18px;
          padding: 20px;
          margin-bottom: 18px;
        }
        .jota-skin-cta-title {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 15px;
          font-weight: 700;
          color: var(--ink);
          margin-bottom: 6px;
        }
        .jota-skin-cta-sub {
          font-size: 13px;
          line-height: 1.5;
          color: var(--ink-soft);
          margin: 0 0 16px;
        }
        .jota-wizard-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 15;
        }
        .jota-wizard-sheet.open {
          transform: translateY(0);
        }
        .jota-wizard-back {
          pointer-events: auto;
          position: absolute;
          top: 16px;
          left: 16px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink-soft);
          width: 32px;
          height: 32px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        /* Header igual ao do chat: fora do fluxo (não empurra o avatar pra baixo),
           botões soltos flutuando em cada lado, por cima do avatar. */
        .jota-wizard-head {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          z-index: 5;
          pointer-events: none;
        }
        .jota-wizard-head-right {
          pointer-events: auto;
          position: absolute;
          top: 16px;
          right: 16px;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 8px;
        }
        /* Botão Salvar (embaixo do X) — só quando editando a análise */
        .jota-wizard-save {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        /* Avatar J-OTA solto e centralizado, logo no topo (o header não ocupa espaço) */
        .jota-wizard-hero {
          display: flex;
          justify-content: center;
          padding: 14px 0 12px;
        }
        /* Tracinhos de progresso abaixo do avatar, com uma folga */
        .jota-wizard-progress {
          margin-top: 4px;
        }
        .jota-wizard-body {
          flex: 1;
          overflow-y: auto;
          padding: 8px 22px 28px;
          display: flex;
          flex-direction: column;
        }
        .jota-wizard-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 20px;
          font-weight: 700;
          color: var(--ink);
          line-height: 1.3;
          margin-bottom: 6px;
        }
        .jota-wizard-sub {
          font-size: 13px;
          color: var(--muted);
          margin-bottom: 20px;
        }
        .jota-wizard-options {
          margin-top: 12px;
        }
        .jota-wizard-options.single {
          display: flex;
          flex-direction: column;
          gap: 10px;
        }
        .jota-wizard-options.multi {
          display: flex;
          flex-wrap: wrap;
          gap: 8px;
        }
        .jota-wizard-option {
          display: flex;
          flex-direction: column;
          gap: 3px;
          background: var(--paper);
          border: 1.5px solid var(--hairline-strong);
          border-radius: 14px;
          padding: 14px 18px;
          text-align: left;
          cursor: pointer;
        }
        .jota-wizard-option-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink);
        }
        .jota-wizard-option-help {
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 400;
          line-height: 1.4;
          color: var(--muted);
        }
        .jota-wizard-option.selected .jota-wizard-option-help {
          color: rgba(255,255,255,0.75);
        }
        .jota-wizard-options.multi .jota-wizard-option {
          border-radius: 100px;
          padding: 10px 16px;
        }
        .jota-wizard-options.multi .jota-wizard-option-title {
          font-size: 13px;
        }
        .jota-wizard-option.selected {
          background: var(--ink);
          border-color: var(--ink);
        }
        .jota-wizard-option.selected .jota-wizard-option-title {
          color: #fff;
        }
        .jota-wizard-continue {
          margin-top: 24px;
        }
        .jota-birthdate-row {
          display: flex;
          gap: 10px;
        }
        .jota-wizard-select {
          flex: 1;
          min-width: 0;
          background: var(--paper);
          border: 1.5px solid var(--hairline-strong);
          border-radius: 14px;
          padding: 14px 10px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-disclaimer-bar {
          display: flex;
          align-items: flex-start;
          gap: 8px;
          width: 100%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 11px 13px;
          margin-bottom: 18px;
          text-align: left;
          cursor: pointer;
        }
        .jota-disclaimer-bar svg {
          flex-shrink: 0;
          margin-top: 1px;
          color: var(--ink-soft);
        }
        .jota-disclaimer-bar span {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          line-height: 1.5;
          color: var(--ink-soft);
        }
        .jota-disclaimer-bar strong {
          color: var(--ink);
          text-decoration: underline;
        }
        .jota-profile-summary {
          margin-bottom: 18px;
        }
        .jota-profile-tags {
          margin-bottom: 14px;
        }
        /* Demais incômodos, agrupados logo abaixo do "Principal incômodo" */
        .jota-profile-fact-tags {
          display: flex;
          flex-wrap: wrap;
          justify-content: flex-end;
          gap: 6px;
          padding: 2px 0 10px;
        }
        .jota-profile-actions {
          display: flex;
          gap: 8px;
          margin-top: 4px;
        }
        .jota-profile-action-btn {
          flex: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 9px 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-profile-facts {
          display: flex;
          flex-direction: column;
        }
        .jota-profile-fact {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 9px 0;
          border-top: 1px solid var(--hairline);
          font-size: 13px;
        }
        .jota-profile-fact:first-child {
          border-top: none;
        }
        .jota-profile-fact-label {
          color: var(--muted);
          font-weight: 500;
        }
        .jota-profile-fact-value {
          color: var(--ink);
          font-weight: 600;
          text-align: right;
        }

        .jota-avatar-label {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.05em;
          text-transform: uppercase;
          color: var(--muted);
          margin: 18px 0 10px;
        }

        .jota-diario-section-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--muted);
          margin: 22px 0 10px;
        }
        .jota-week-strip {
          display: flex;
          justify-content: space-between;
          align-items: flex-end;
          gap: 8px;
        }
        .jota-week-bar-col {
          flex: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 8px;
        }
        .jota-week-bar-count {
          font-family: 'Open Sans', sans-serif;
          font-size: 10px;
          font-weight: 700;
          color: var(--ink-soft);
          line-height: 1;
          min-height: 10px;
        }
        .jota-week-bar-track {
          width: 100%;
          height: 56px;
          background: var(--fog);
          border-radius: 8px;
          display: flex;
          align-items: flex-end;
          overflow: hidden;
        }
        .jota-week-bar-fill {
          width: 100%;
          background: var(--ink);
          border-radius: 8px 8px 0 0;
          min-height: 3px;
          transition: height 0.3s ease;
        }
        .jota-week-bar-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 10px;
          font-weight: 600;
          color: var(--muted);
        }
        .jota-week-bar-label.today {
          color: var(--ink);
          font-weight: 700;
        }
        .jota-diary-cta-banner {
          position: relative;
          overflow: hidden;
          isolation: isolate;
          display: flex;
          align-items: center;
          gap: 14px;
          width: 100%;
          background: linear-gradient(135deg, #131316 0%, #3A3A42 100%);
          border: none;
          border-radius: 20px;
          padding: 20px 20px;
          margin-bottom: 18px;
          cursor: pointer;
          color: #fff;
          text-align: left;
          box-shadow: 0 10px 26px rgba(20,20,24,0.18),
                      inset 0 1px 1px rgba(255,255,255,0.22),
                      inset 0 0 0 1px rgba(255,255,255,0.08);
        }
        /* reflexo de vidro no topo (mesmo dos demais banners) */
        .jota-diary-cta-banner::after {
          content: '';
          position: absolute;
          top: 0; left: 0; right: 0;
          height: 46%;
          z-index: 0;
          pointer-events: none;
          background: linear-gradient(180deg, rgba(255,255,255,0.16) 0%, rgba(255,255,255,0.04) 42%, transparent 100%);
        }
        .jota-diary-cta-banner > * { position: relative; z-index: 1; }
        .jota-diary-cta-icon {
          flex-shrink: 0;
          width: 46px;
          height: 46px;
          border-radius: 50%;
          background: rgba(255,255,255,0.15);
          display: flex;
          align-items: center;
          justify-content: center;
          color: #fff;
        }
        .jota-diary-cta-text {
          display: flex;
          flex-direction: column;
          gap: 3px;
        }
        .jota-diary-cta-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 16px;
          font-weight: 700;
          line-height: 1.25;
          color: #fff;
        }
        .jota-diary-cta-sub {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          color: rgba(255,255,255,0.72);
        }
        .jota-diary-history-head {
          display: flex;
          align-items: center;
          justify-content: space-between;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 10px;
        }
        /* base do bloco de registros equilibrada com o topo (o .jota-card padrão
           deixa só 6px embaixo, o que empurra o último registro contra a borda) */
        .jota-diary-rows-card {
          padding-bottom: 16px;
        }
        /* Histórico do diário em linhas (infos à esquerda, foto à direita) */
        .jota-diary-rows {
          display: flex;
          flex-direction: column;
          gap: 8px;
        }
        .jota-diary-row {
          display: flex;
          align-items: center;
          gap: 12px;
          width: 100%;
          box-sizing: border-box;
          text-align: left;
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 14px;
          padding: 10px 12px;
          cursor: pointer;
        }
        .jota-diary-row-info {
          flex: 1;
          display: flex;
          flex-direction: column;
          gap: 3px;
          min-width: 0;
        }
        .jota-diary-row-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        .jota-diary-row-meta {
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--muted);
        }
        .jota-diary-row-photo {
          position: relative;
          width: 56px;
          height: 56px;
          flex-shrink: 0;
          border-radius: 10px;
          overflow: hidden;
          background: var(--fog);
          transition: width 0.15s ease, height 0.15s ease;
        }
        /* Editando registros: encolhe a miniatura pra abrir espaço pros ícones de ação */
        .jota-diary-row-photo-compact {
          width: 36px;
          height: 36px;
        }
        .jota-diary-row-photo img {
          width: 100%;
          height: 100%;
          object-fit: cover;
          display: block;
        }
        .jota-diary-more-btn {
          width: 100%;
          margin-top: 10px;
          background: none;
          border: 1px solid var(--hairline-strong);
          border-radius: 12px;
          padding: 11px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-diary-title-input {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 12px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          color: var(--ink);
          outline: none;
          margin-bottom: 14px;
        }
        .jota-diario-week-title {
          margin-top: 24px;
        }
        /* Painel de Evolução: grid 2x2 de números (dashboard) */
        .jota-evo-grid {
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          gap: 10px;
        }
        .jota-evo-stat {
          display: flex;
          flex-direction: column;
          gap: 6px;
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 16px 16px 14px;
        }
        .jota-evo-stat-icon {
          width: 30px;
          height: 30px;
          border-radius: 50%;
          background: var(--fog);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-evo-stat-value {
          font-family: 'Open Sans', sans-serif;
          font-size: 24px;
          font-weight: 800;
          color: var(--ink);
          line-height: 1.1;
        }
        .jota-evo-stat-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          color: var(--muted);
          line-height: 1.3;
        }
        .jota-diary-view-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 17px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-diary-thumb {
          position: relative;
          aspect-ratio: 1;
          border-radius: 12px;
          overflow: hidden;
          border: 2px solid transparent;
          background: var(--fog);
          padding: 0;
          cursor: pointer;
        }
        .jota-diary-thumb img {
          width: 100%;
          height: 100%;
          object-fit: cover;
          display: block;
        }
        .jota-diary-thumb.selected {
          border-color: var(--ink);
        }
        .jota-diary-thumb-empty {
          width: 100%;
          height: 100%;
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--muted);
        }
        .jota-diary-thumb-date {
          position: absolute;
          bottom: 0;
          left: 0;
          right: 0;
          background: linear-gradient(to top, rgba(20,20,24,0.7), transparent);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 9px;
          font-weight: 600;
          padding: 10px 6px 5px;
          text-align: left;
        }

        .jota-diary-view-overlay {
          position: absolute;
          inset: 0;
          background: rgba(20,20,24,0.5);
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 24px;
          z-index: 8;
        }
        .jota-diary-view-card {
          position: relative;
          background: var(--paper);
          border-radius: 20px;
          padding: 20px;
          width: 100%;
          max-height: 85%;
          overflow-y: auto;
        }
        .jota-diary-view-close {
          position: absolute;
          top: 12px;
          right: 12px;
          z-index: 2;
        }
        .jota-diary-view-photo {
          width: 100%;
          border-radius: 14px;
          margin-bottom: 12px;
          display: block;
        }
        .jota-diary-view-date {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          color: var(--muted);
          margin-bottom: 8px;
        }
        .jota-diary-view-note {
          font-size: 13.5px;
          line-height: 1.55;
          color: var(--ink-soft);
          margin: 0 0 14px;
        }
        .jota-diary-compare-card {
          position: relative;
          background: var(--paper);
          border-radius: 20px;
          padding: 20px;
          width: 100%;
          display: flex;
          gap: 10px;
        }
        .jota-diary-compare-col {
          flex: 1;
          display: flex;
          flex-direction: column;
          gap: 6px;
          align-items: center;
        }
        .jota-diary-compare-col img {
          width: 100%;
          aspect-ratio: 1;
          object-fit: cover;
          border-radius: 12px;
        }
        .jota-diary-compare-col span {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          color: var(--muted);
        }

        .jota-diary-post-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 16;
        }
        .jota-diary-post-sheet.open {
          transform: translateY(0);
        }
        .jota-diary-post-body {
          flex: 1;
          overflow-y: auto;
          /* mesma distância cabeçalho→conteúdo padronizada em todas as sheets (23px, igual à Rotina) */
          padding: 23px 22px 28px;
        }
        .jota-diary-photo-row {
          display: flex;
          flex-wrap: wrap;
          gap: 8px;
          margin-bottom: 18px;
        }
        .jota-diary-photo-thumb {
          position: relative;
          width: 76px;
          height: 76px;
          border-radius: 14px;
          overflow: hidden;
        }
        .jota-diary-photo-thumb img {
          width: 100%;
          height: 100%;
          object-fit: cover;
          display: block;
        }
        .jota-diary-photo-remove {
          position: absolute;
          top: 4px;
          right: 4px;
          width: 18px;
          height: 18px;
          border-radius: 50%;
          background: rgba(20,20,24,0.7);
          color: #fff;
          border: none;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-diary-photo-add {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          gap: 4px;
          width: 76px;
          height: 76px;
          background: var(--fog);
          border: 1.5px dashed var(--hairline-strong);
          border-radius: 14px;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 9.5px;
          font-weight: 600;
          text-align: center;
          cursor: pointer;
          padding: 4px;
        }
        .jota-diary-view-photos {
          display: flex;
          flex-direction: column;
          gap: 8px;
          margin-bottom: 12px;
        }
        .jota-diary-view-tags {
          margin-bottom: 4px;
        }
        .jota-diary-view-actions {
          display: flex;
          gap: 10px;
          margin-top: 16px;
        }
        .jota-diary-edit-btn,
        .jota-diary-delete-btn {
          flex: 1;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 6px;
          padding: 10px 0;
          border-radius: 100px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          cursor: pointer;
        }
        .jota-diary-edit-btn {
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          color: var(--ink-soft);
        }
        .jota-diary-delete-btn {
          background: none;
          border: 1px solid var(--hairline-strong);
          color: var(--ink-soft);
        }
        .jota-diary-thumb-count {
          position: absolute;
          top: 6px;
          right: 6px;
          background: rgba(20,20,24,0.75);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 9px;
          font-weight: 700;
          padding: 2px 6px;
          border-radius: 100px;
        }
        .jota-diary-textarea {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 14px;
          padding: 12px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          color: var(--ink);
          resize: none;
          outline: none;
          margin: 10px 0 18px;
        }
        .jota-diary-textarea::placeholder {
          color: var(--muted);
        }
        .jota-diary-hint {
          font-size: 12px;
          color: var(--muted);
          margin: 0 0 18px;
        }
        .jota-diary-save {
          margin-top: 24px;
        }
        .jota-empty {
          border: 1px dashed var(--hairline-strong);
          border-radius: 20px;
          padding: 32px 20px;
          text-align: center;
        }
        .jota-empty-title {
          font-family: 'Open Sans', sans-serif;
          font-weight: 600;
          font-size: 14.5px;
          color: var(--ink-soft);
          margin-bottom: 4px;
        }
        .jota-empty-sub {
          font-size: 12.5px;
          color: var(--muted);
        }

        /* Ícone de filtros dentro da própria barra de busca (lado direito) */
        .jota-search-filter-btn {
          display: flex;
          align-items: center;
          gap: 5px;
          flex-shrink: 0;
          background: none;
          border: none;
          padding: 0;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-filters-count {
          background: var(--ink);
          color: #fff;
          border-radius: 100px;
          font-size: 10px;
          font-weight: 700;
          padding: 1px 6px;
        }
        .jota-filters-panel {
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 14px;
          padding: 12px;
          margin-bottom: 16px;
        }
        /* Grupos de filtro viram acordeões (menu suspenso) */
        .jota-filters-group {
          border-top: 1px solid var(--hairline);
        }
        .jota-filters-group:first-child {
          border-top: none;
        }
        .jota-filters-group-head {
          width: 100%;
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 10px;
          background: none;
          border: none;
          padding: 11px 2px;
          cursor: pointer;
          text-align: left;
        }
        .jota-filters-group-meta {
          display: flex;
          align-items: center;
          gap: 8px;
          color: var(--muted);
          flex-shrink: 0;
        }
        .jota-filters-chevron {
          transition: transform 0.18s ease;
        }
        .jota-filters-group.open .jota-filters-chevron {
          transform: rotate(180deg);
        }
        .jota-filters-group-body {
          padding: 2px 2px 12px;
        }
        .jota-filters-label {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 6px;
        }
        .jota-filters-group-head .jota-filters-label {
          margin-bottom: 0;
        }
        /* Ativos: seletor de modo (todos / pelo menos um) */
        .jota-ativo-mode {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 8px;
          flex-wrap: wrap;
          margin-bottom: 10px;
        }
        .jota-ativo-mode-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          color: var(--ink-soft);
        }
        .jota-ativo-mode-toggle {
          display: inline-flex;
          background: var(--paper);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 2px;
        }
        .jota-ativo-mode-toggle button {
          border: none;
          background: none;
          border-radius: 100px;
          padding: 5px 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--muted);
          cursor: pointer;
        }
        .jota-ativo-mode-toggle button.active {
          background: var(--ink);
          color: #fff;
        }
        /* Chip de ativo com selo de uso profissional */
        .jota-recur-chip.pro {
          display: inline-flex;
          align-items: center;
          gap: 4px;
        }
        .jota-ativo-pro-icon {
          color: #C77E1A;
        }
        .jota-recur-chip.pro.active .jota-ativo-pro-icon {
          color: #F0C27B;
        }
        .jota-ativo-legend {
          display: flex;
          align-items: center;
          gap: 5px;
          margin-top: 10px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
        }
        .jota-ativo-legend svg {
          color: #C77E1A;
          flex-shrink: 0;
        }
        /* Nota educativa de uso profissional na página do produto */
        .jota-pro-note {
          display: flex;
          gap: 10px;
          align-items: flex-start;
          background: #FDF6EC;
          border: 1px solid #F0D9B5;
          border-radius: 12px;
          padding: 12px 14px;
          margin-top: 14px;
        }
        .jota-pro-note > svg {
          color: #C77E1A;
          flex-shrink: 0;
          margin-top: 1px;
        }
        .jota-pro-note-text {
          display: flex;
          flex-direction: column;
          gap: 3px;
        }
        .jota-pro-note-text strong {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          color: #8A5A12;
        }
        .jota-pro-note-text span {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          line-height: 1.5;
          color: var(--ink-soft);
        }
        .jota-filters-hint {
          text-transform: none;
          font-weight: 500;
          letter-spacing: 0;
          opacity: 0.75;
        }
        .jota-filters-options {
          display: flex;
          flex-wrap: wrap;
          gap: 6px;
        }
        .jota-filters-actions {
          display: flex;
          gap: 8px;
          margin-top: 4px;
          padding-top: 12px;
          border-top: 1px solid var(--hairline-strong);
        }
        .jota-filters-clear {
          flex: 1;
          background: none;
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 10px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-filters-save {
          flex: 1;
          background: var(--ink);
          border: 1px solid var(--ink);
          border-radius: 100px;
          padding: 10px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          color: #fff;
          cursor: pointer;
        }

        .jota-price-slider {
          padding-top: 2px;
        }
        .jota-price-values {
          display: flex;
          justify-content: space-between;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          color: var(--ink);
          margin-bottom: 12px;
        }
        .jota-price-track {
          position: relative;
          height: 4px;
          background: var(--hairline-strong);
          border-radius: 100px;
          margin: 0 9px;
        }
        .jota-price-fill {
          position: absolute;
          top: 0;
          height: 100%;
          background: var(--ink);
          border-radius: 100px;
        }
        .jota-price-thumb {
          position: absolute;
          top: 50%;
          width: 18px;
          height: 18px;
          margin-left: -9px;
          transform: translateY(-50%);
          background: #fff;
          border: 2px solid var(--ink);
          border-radius: 50%;
          cursor: grab;
          touch-action: none;
          padding: 0;
        }
        .jota-price-thumb:active {
          cursor: grabbing;
        }

        .jota-pgrid {
          display: grid;
          grid-template-columns: 1fr 1fr;
          gap: 10px;
          margin-bottom: 18px;
        }
        .jota-pcard {
          display: flex;
          flex-direction: column;
          align-items: flex-start;
          gap: 8px;
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 16px;
          padding: 12px;
          cursor: pointer;
          text-align: left;
        }
        .jota-pcard-photo {
          position: relative;
          width: 100%;
          display: flex;
          justify-content: center;
          padding: 8px 0 10px;
        }
        .jota-card-tag-actions {
          position: absolute;
          top: 4px;
          right: 4px;
          display: flex;
          flex-direction: column;
          gap: 4px;
        }
        .jota-tag-btn {
          width: 22px;
          height: 22px;
          border-radius: 50%;
          background: rgba(255,255,255,0.92);
          border: 1px solid var(--hairline);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          color: var(--ink-soft);
        }
        .jota-tag-btn.active {
          color: var(--ink);
        }
        /* Status sem cor: ícone preto (aprovado=check, reprovado=bloqueio, testando=flask preenchida) */
        .jota-status-btn.status-testando,
        .jota-status-btn.status-aprovado,
        .jota-status-btn.status-reprovado {
          color: var(--ink);
        }
        .jota-pcard-body {
          display: flex;
          flex-direction: column;
          gap: 3px;
          width: 100%;
        }
        .jota-pcard-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink);
          line-height: 1.25;
        }
        .jota-pcard-rating {
          display: flex;
          align-items: center;
          gap: 3px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-pcard-rating-count {
          font-weight: 500;
          color: var(--muted);
        }
        .jota-pcard-price {
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 700;
          color: var(--ink);
        }

        .jota-suggest-footer {
          text-align: center;
          padding: 4px 0 6px;
        }
        .jota-suggest-link {
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 500;
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-suggest-form {
          display: flex;
          align-items: center;
          gap: 8px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 6px 6px 6px 14px;
        }
        .jota-suggest-input {
          padding: 0;
        }
        .jota-suggest-sent {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          color: var(--ink-soft);
        }

        .jota-product-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 12;
        }
        .jota-product-sheet.open {
          transform: translateY(0);
        }
        .jota-pdetail-body {
          position: relative;
          flex: 1;
          overflow-y: auto;
          /* mesma distância cabeçalho→conteúdo da Rotina (23px), padronizada em todas as sheets */
          padding: 23px 24px 28px;
        }
        .jota-pdetail-photo {
          display: flex;
          justify-content: center;
          padding: 6px 0 18px;
        }
        .jota-pdetail-photo-inner {
          position: relative;
          display: inline-flex;
        }
        .jota-pdetail-brand {
          display: block;
          width: 100%;
          background: none;
          border: none;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          text-transform: uppercase;
          letter-spacing: 0.04em;
          color: var(--muted);
          text-decoration: underline;
          text-align: center;
          cursor: pointer;
        }
        .jota-brand-note {
          text-align: center;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
          margin-top: 4px;
        }
        .jota-pdetail-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 19px;
          font-weight: 700;
          color: var(--ink);
          text-align: center;
          margin: 3px 0 8px;
        }
        .jota-pdetail-rating {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 4px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
          margin-bottom: 4px;
        }
        .jota-pdetail-price {
          text-align: center;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          color: var(--muted);
          margin-bottom: 16px;
        }
        .jota-accordion {
          border-top: 1px solid var(--hairline);
          margin-bottom: 20px;
        }
        .jota-accordion-item {
          border-bottom: 1px solid var(--hairline);
        }
        .jota-accordion-head {
          display: flex;
          align-items: center;
          justify-content: space-between;
          width: 100%;
          background: none;
          border: none;
          padding: 14px 2px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-accordion-chevron {
          color: var(--muted);
          transition: transform 0.2s ease;
        }
        .jota-accordion-chevron.open {
          transform: rotate(180deg);
        }
        .jota-accordion-body {
          padding: 0 2px 16px;
        }
        .jota-accordion-text {
          font-size: 13.5px;
          line-height: 1.55;
          color: var(--ink-soft);
          margin: 0;
        }
        .jota-ingredient-list {
          margin: 0;
          padding-left: 18px;
          font-size: 13.5px;
          line-height: 1.7;
          color: var(--ink-soft);
        }
        .jota-report-footer {
          text-align: center;
          margin-top: 16px;
        }
        .jota-report-link {
          display: inline-flex;
          align-items: center;
          gap: 6px;
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 500;
          cursor: pointer;
        }
        .jota-pdetail-tags {
          display: flex;
          flex-wrap: wrap;
          gap: 6px;
          margin-bottom: 22px;
        }
        .jota-tag {
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 5px 11px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 500;
          color: var(--ink-soft);
        }
        .jota-tag-period {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-tag-inline {
          margin-left: 8px;
          vertical-align: middle;
          font-size: 10px;
          padding: 3px 9px;
        }
        .jota-tag-row {
          display: flex;
          flex-wrap: wrap;
          gap: 6px;
        }

        /* Ações no topo direito da página do produto: favoritar + compartilhar */
        .jota-pdetail-actions {
          position: absolute;
          top: 6px;
          right: 20px;
          display: flex;
          flex-direction: column;
          gap: 8px;
          z-index: 5;
        }
        .jota-round-action {
          width: 38px;
          height: 38px;
          border-radius: 50%;
          background: var(--paper);
          border: 1px solid var(--hairline);
          box-shadow: 0 2px 8px rgba(45,50,80,0.10);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          color: var(--ink-soft);
        }
        .jota-round-action.active {
          color: var(--ink);
          border-color: var(--hairline-strong);
        }
        .jota-share-wrap {
          position: relative;
        }
        .jota-share-menu {
          position: absolute;
          top: 0;
          right: calc(100% + 8px);
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 14px;
          box-shadow: 0 8px 24px rgba(45,50,80,0.16);
          padding: 6px;
          display: flex;
          flex-direction: column;
          gap: 2px;
          width: 168px;
          z-index: 6;
        }
        .jota-share-opt {
          display: flex;
          align-items: center;
          gap: 9px;
          width: 100%;
          background: none;
          border: none;
          border-radius: 9px;
          padding: 9px 11px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
          text-align: left;
        }
        .jota-share-opt:hover {
          background: var(--fog);
        }

        .jota-pdetail-rating-sep {
          color: var(--muted);
        }
        .jota-pdetail-brand-inline {
          background: none;
          border: none;
          padding: 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          text-transform: uppercase;
          letter-spacing: 0.03em;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-pdetail-desc {
          font-size: 13.5px;
          line-height: 1.6;
          color: var(--ink-soft);
          text-align: center;
          margin: 0 0 20px;
        }
        .jota-review-anon {
          display: flex;
          align-items: center;
          gap: 8px;
          margin: 4px 0 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 500;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-review-anon input {
          width: 16px;
          height: 16px;
          accent-color: var(--ink);
          cursor: pointer;
        }

        /* Timeline de avaliações da comunidade */
        .jota-reviews-filter {
          display: flex;
          flex-wrap: wrap;
          gap: 6px;
          margin-bottom: 14px;
        }
        .jota-reviews-chip {
          display: inline-flex;
          align-items: center;
          gap: 3px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 5px 11px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-reviews-chip.active {
          background: var(--ink);
          border-color: var(--ink);
          color: #fff;
        }
        .jota-reviews-empty {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          line-height: 1.5;
          color: var(--muted);
          margin: 4px 0 0;
        }
        .jota-reviews-list {
          display: flex;
          flex-direction: column;
          gap: 14px;
        }
        .jota-review-item {
          border: 1px solid var(--hairline);
          border-radius: 14px;
          padding: 12px 14px;
        }
        .jota-review-item-head {
          display: flex;
          align-items: center;
          gap: 10px;
          margin-bottom: 8px;
        }
        .jota-review-avatar {
          width: 30px;
          height: 30px;
          border-radius: 50%;
          background: var(--ink);
          color: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          flex-shrink: 0;
        }
        .jota-review-avatar.anon {
          background: var(--hairline-strong);
          color: var(--ink-soft);
        }
        .jota-review-item-meta {
          display: flex;
          flex-direction: column;
          gap: 2px;
          min-width: 0;
        }
        .jota-review-item-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-review-item-stars {
          display: flex;
          align-items: center;
          gap: 2px;
          color: var(--ink);
        }
        .jota-review-item-date {
          margin-left: 5px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 500;
          color: var(--muted);
        }
        .jota-review-item-text {
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          line-height: 1.55;
          color: var(--ink-soft);
          margin: 0;
          white-space: pre-wrap;
        }
        .jota-pdetail-myrating {
          border-top: 1px solid var(--hairline);
          border-bottom: 1px solid var(--hairline);
          padding: 16px 0;
          margin-bottom: 20px;
          text-align: center;
        }
        .jota-pdetail-myrating-label {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--muted);
          margin-bottom: 8px;
        }
        .jota-star-picker {
          display: flex;
          justify-content: center;
          gap: 6px;
        }
        .jota-star-picker button {
          background: none;
          border: none;
          color: var(--ink);
          cursor: pointer;
          padding: 2px;
        }
        .jota-review-form {
          margin-top: 14px;
          text-align: left;
        }
        .jota-review-textarea {
          width: 100%;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 14px;
          padding: 12px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          color: var(--ink);
          resize: none;
          outline: none;
          margin-bottom: 8px;
        }
        .jota-review-textarea::placeholder {
          color: var(--muted);
        }
        .jota-review-submit {
          width: 100%;
          background: var(--ink);
          color: #fff;
          border: none;
          border-radius: 100px;
          padding: 11px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          cursor: pointer;
        }
        .jota-review-submit:disabled {
          background: var(--hairline-strong);
          color: var(--muted);
          cursor: default;
        }
        .jota-review-sent {
          margin-top: 12px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-pdetail-buy {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 8px;
          background: var(--ink);
          color: #fff;
          border-radius: 100px;
          padding: 14px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          text-decoration: none;
        }

        .jota-orb-wrap {
          position: relative;
          display: flex;
          align-items: center;
          justify-content: center;
          transform: translateZ(0);
          -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
        }
        .jota-orb-shell {
          position: absolute;
          inset: 0;
          margin: auto;
          width: 90%;
          height: 90%;
          border-radius: 50%;
          z-index: 3;
          pointer-events: none;
          background: radial-gradient(circle at 50% 42%, rgba(255,255,255,0) 56%, rgba(255,255,255,0.06) 88%, rgba(255,255,255,0) 100%);
          border: 2px solid rgba(255,255,255,0.92);
          box-shadow:
            inset 0 2px 8px rgba(255,255,255,0.6),
            inset 0 -4px 11px rgba(120,140,180,0.18),
            0 0 2px rgba(255,255,255,0.9),
            0 0 7px 2px rgba(255,255,255,0.6),
            0 0 13px 5px rgba(255,255,255,0.32),
            0 6px 13px rgba(45,50,80,0.14),
            0 13px 26px rgba(45,50,80,0.1);
        }
        .jota-orb-core {
          position: absolute;
          inset: 0;
          margin: auto;
          z-index: 2;
          overflow: hidden;
          isolation: isolate;
          border-radius: 42% 58% 61% 39% / 44% 45% 55% 56%;
          -webkit-clip-path: inset(0 round 42% 58% 61% 39% / 44% 45% 55% 56%);
          clip-path: inset(0 round 42% 58% 61% 39% / 44% 45% 55% 56%);
          background:
            radial-gradient(circle at 22% 14%, rgba(255,255,255,0.72), rgba(255,255,255,0) 24%),
            radial-gradient(circle at 78% 12%, rgba(255,35,190,0.95), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 95% 40%, rgba(160,60,255,0.95), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 88% 74%, rgba(25,105,255,0.95), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 55% 92%, rgba(0,225,205,0.94), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 20% 87%, rgba(10,185,255,0.94), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 4% 52%, rgba(0,212,235,0.95), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 12% 18%, rgba(78,88,255,0.94), rgba(255,255,255,0) 56%),
            radial-gradient(circle at 68% 54%, rgba(255,120,40,0.62), rgba(255,255,255,0) 38%),
            radial-gradient(circle at 38% 78%, rgba(255,180,55,0.56), rgba(255,255,255,0) 36%),
            radial-gradient(circle at 84% 62%, rgba(255,75,60,0.46), rgba(255,255,255,0) 30%),
            radial-gradient(circle at 30% 42%, rgba(255,155,65,0.36), rgba(255,255,255,0) 26%),
            radial-gradient(circle at 50% 50%, rgba(140,140,238,0.55), rgba(145,155,232,0.32) 100%);
          border: 1px solid rgba(255,255,255,0.65);
          box-shadow:
            inset 0 1px 4px rgba(255,255,255,0.85),
            inset -3px -4px 8px rgba(120,130,160,0.16),
            inset 4px 5px 9px rgba(255,255,255,0.3);
          transform: translateZ(0);
          -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
          will-change: transform;
          animation: jota-breathe 4s ease-in-out infinite;
        }
        .jota-orb-core::before {
          content: '';
          position: absolute;
          inset: -15%;
          background:
            radial-gradient(circle at 88% 46%, rgba(175,110,255,0.52), rgba(175,110,255,0) 42%),
            radial-gradient(circle at 74% 80%, rgba(70,135,255,0.5), rgba(70,135,255,0) 40%),
            radial-gradient(circle at 45% 90%, rgba(45,225,215,0.5), rgba(45,225,215,0) 42%),
            radial-gradient(circle at 20% 78%, rgba(60,200,255,0.5), rgba(60,200,255,0) 40%),
            radial-gradient(circle at 10% 45%, rgba(40,220,230,0.52), rgba(40,220,230,0) 42%),
            radial-gradient(circle at 24% 18%, rgba(110,120,255,0.5), rgba(110,120,255,0) 40%),
            radial-gradient(circle at 55% 11%, rgba(255,95,205,0.5), rgba(255,95,205,0) 42%),
            radial-gradient(circle at 82% 22%, rgba(255,160,90,0.5), rgba(255,160,90,0) 40%);
          will-change: transform;
          animation: jota-swirl 16s linear infinite;
        }
        .jota-orb-core::after {
          content: '';
          position: absolute;
          top: 9%;
          left: 14%;
          width: 37%;
          height: 25%;
          border-radius: 50%;
          background: radial-gradient(ellipse at center, rgba(255,255,255,1), rgba(255,255,255,0.72) 48%, rgba(255,255,255,0) 80%);
          filter: blur(5px);
          transform: rotate(-18deg);
        }
        @keyframes jota-swirl {
          from { transform: rotate(0deg); }
          to { transform: rotate(360deg); }
        }
        .jota-orb-shimmer {
          position: absolute;
          inset: -10%;
          background:
            radial-gradient(circle at 28% 22%, rgba(255,255,255,0.5), rgba(255,255,255,0) 44%),
            radial-gradient(circle at 78% 34%, rgba(45,220,235,0.46), rgba(45,220,235,0) 44%),
            radial-gradient(circle at 62% 82%, rgba(255,100,210,0.44), rgba(255,100,210,0) 44%),
            radial-gradient(circle at 16% 68%, rgba(110,140,255,0.46), rgba(110,140,255,0) 44%);
          opacity: 0.55;
          will-change: transform;
          animation: jota-swirl 9s linear infinite reverse;
        }
        .jota-orb-rim {
          position: absolute;
          inset: 0;
          border-radius: inherit;
          background: conic-gradient(from 0deg,
            rgba(255,90,200,0.92),
            rgba(175,105,255,0.92) 15%,
            rgba(90,130,255,0.92) 32%,
            rgba(40,215,235,0.9) 48%,
            rgba(55,225,205,0.9) 62%,
            rgba(255,175,95,0.82) 73%,
            rgba(255,120,170,0.88) 84%,
            rgba(255,90,200,0.92));
          -webkit-mask: radial-gradient(closest-side, transparent 68%, #000 87%);
          mask: radial-gradient(closest-side, transparent 68%, #000 87%);
          filter: blur(1.1px) saturate(1.6);
          mix-blend-mode: overlay;
          will-change: transform;
          animation: jota-swirl 12s linear infinite;
          pointer-events: none;
        }
        .jota-orb-wrap.speaking .jota-orb-core {
          animation: jota-breathe-speak 0.9s ease-in-out infinite;
        }
        @keyframes jota-breathe-speak {
          0%, 100% { transform: scale(1); }
          50% { transform: scale(1.14); }
        }
        .jota-orb-atom {
          position: absolute;
          inset: 0;
          transform: rotate(-15deg);
          animation: jota-orbit-z 22s step-end infinite;
          pointer-events: none;
        }
        .jota-orb-dot3d {
          position: absolute;
          left: 0;
          top: 0;
          width: 7%;
          aspect-ratio: 1;
          border-radius: 50%;
          background: #FFFFFF;
          box-shadow: 0 0 4px 1px rgba(255,255,255,0.95), 0 0 10px 3px rgba(190,210,255,0.6);
          offset-path: ellipse(47% 26% at 50% 40%);
          offset-rotate: 0deg;
          will-change: transform;
          animation: jota-orbit 22s linear infinite;
          pointer-events: none;
        }
        /* offset-path com basic-shape só é suportado em Safari recente;
           em navegadores antigos a bolinha ficaria parada num canto — nesse
           caso escondemos ela (degradação graciosa, o resto do orbe funciona). */
        @supports not (offset-path: ellipse(1% 1% at 50% 50%)) {
          .jota-orb-atom { display: none; }
        }
        @keyframes jota-orbit {
          from { offset-distance: 0%; }
          to { offset-distance: 100%; }
        }
        @keyframes jota-orbit-z {
          0% { z-index: 4; }
          50% { z-index: 1; }
          100% { z-index: 4; }
        }
        @keyframes jota-breathe {
          0%, 100% { transform: scale(1, 1) rotate(0deg) translateZ(0); }
          33%      { transform: scale(1.055, 1.02) rotate(0.6deg) translateZ(0); }
          66%      { transform: scale(1.02, 1.055) rotate(-0.6deg) translateZ(0); }
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-orb-core,
          .jota-orb-core::before,
          .jota-orb-shimmer,
          .jota-orb-rim,
          .jota-orb-dot3d,
          .jota-orb-atom { animation: none !important; }
        }

        .jota-navbar {
          position: absolute;
          bottom: 0;
          left: 0;
          right: 0;
          height: 78px;
          /* vidro: mesma linguagem do header, um pouco mais transparente */
          background: rgba(255,255,255,0.55);
          -webkit-backdrop-filter: blur(22px) saturate(180%);
          backdrop-filter: blur(22px) saturate(180%);
          /* sem linha cinza: só um fio branco sutil + a sombra */
          border-top: 1px solid rgba(255,255,255,0.55);
          /* cantos de cima bem arredondados */
          border-top-left-radius: 32px;
          border-top-right-radius: 32px;
          box-shadow: 0 -10px 30px rgba(40,45,70,0.08);
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 0 14px;
          box-sizing: border-box;
          z-index: 6;
          transition: transform 0.22s ease, opacity 0.22s ease;
        }
        /* Teclado aberto: navbar sai de cena (não fica flutuando sobre o teclado) */
        .jota-navbar.kb-hidden {
          transform: translateY(120%);
          opacity: 0;
          pointer-events: none;
        }
        .jota-navgroup {
          display: flex;
          gap: 22px;
        }
        .jota-navbtn {
          background: none;
          border: none;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 4px;
          cursor: pointer;
          color: var(--muted);
          padding: 6px 2px;
        }
        .jota-navbtn.active {
          color: var(--ink);
        }
        .jota-navbtn span {
          font-size: 10px;
          font-family: 'Open Sans', sans-serif;
          font-weight: 500;
          white-space: nowrap;
        }
        /* Aba bloqueada: cinza esmaecido, sem navegar até ser liberada */
        .jota-navbtn-locked {
          opacity: 0.4;
          cursor: default;
        }
        .jota-navcenter {
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, calc(-50% - 48px));
          background: none;
          border: none;
          cursor: pointer;
          -webkit-tap-highlight-color: transparent;
          outline: none;
          transition: transform 0.18s ease;
          z-index: 1;
        }
        .jota-navcenter:active {
          transform: translate(-50%, calc(-50% - 48px)) scale(1.12);
        }

        .jota-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(${expanded ? "0" : "100%"});
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 10;
        }
        .jota-sheet-head {
          position: relative;
          z-index: 2;
          display: flex;
          align-items: center;
          gap: 10px;
          /* cobre o notch e ancora no topo absoluto (como o header principal):
             a margem negativa "consome" o padding de safe-area da sheet, e o
             padding-top devolve o respiro pro conteúdo — o fundo vai até o topo. */
          margin-top: calc(-1 * env(safe-area-inset-top, 0px));
          padding: calc(20px + env(safe-area-inset-top, 0px)) 18px 16px;
          background: var(--paper);
          /* padrão do app: sem linha cinza — fio branco sutil + sombrinha */
          border-bottom: 1px solid rgba(255,255,255,0.55);
          box-shadow: 0 8px 22px rgba(40,45,70,0.06);
          /* mesmos cantos inferiores arredondados do header principal */
          border-bottom-left-radius: 26px;
          border-bottom-right-radius: 26px;
        }
        .jota-sheet-head-icon {
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--ink);
          flex-shrink: 0;
        }
        .jota-sheet-name {
          font-family: 'Open Sans', sans-serif;
          font-weight: 700;
          font-size: 15.5px;
          color: var(--ink);
        }
        .jota-sheet-close {
          margin-left: auto;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink-soft);
          width: 32px;
          height: 32px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-chat-info-btn {
          background: none;
          border: none;
          color: var(--muted);
          width: 32px;
          height: 32px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-chat-info-btn:hover {
          color: var(--ink-soft);
          background: var(--fog);
        }
        .jota-chat-head {
          position: absolute;
          /* respiro do notch (iPhone) pra não cortar os ícones no topo */
          top: calc(env(safe-area-inset-top, 0px) + 6px);
          left: 0;
          right: 0;
          z-index: 6;
          pointer-events: none;
        }
        /* Avatar do J-OTA no topo — aparece quando a conversa começa, "descendo"
           do centro (encolhe e sobe) pra virar o menor no topo. */
        .jota-chat-avatar {
          position: absolute;
          top: 12px;
          left: 50%;
          transform: translateX(-50%);
          pointer-events: none;
          filter: drop-shadow(0 2px 8px rgba(60,50,120,0.18));
          animation: jota-avatar-rise 0.52s cubic-bezier(0.22, 1, 0.36, 1) both;
        }
        @keyframes jota-avatar-rise {
          from { transform: translateX(-50%) translateY(240px) scale(3.4); opacity: 0.4; }
          60%  { opacity: 1; }
          to   { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-chat-avatar { animation: none; }
        }
        .jota-chat-close {
          pointer-events: auto;
          position: absolute;
          top: 16px;
          left: 16px;
          width: 38px;
          height: 38px;
          border-radius: 50%;
          background: var(--paper);
          border: 1px solid var(--hairline);
          box-shadow: 0 2px 8px rgba(45,50,80,0.10);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-chat-actions {
          pointer-events: auto;
          position: absolute;
          top: 16px;
          right: 16px;
          display: flex;
          flex-direction: column;
          align-items: flex-end;
          gap: 8px;
        }
        /* Contador sutil de mensagens do dia (vira temporizador no limite) */
        .jota-chat-limit-pill {
          display: inline-flex;
          align-items: center;
          gap: 4px;
          height: 24px;
          padding: 0 10px;
          border-radius: 100px;
          background: var(--paper);
          border: 1px solid var(--hairline);
          box-shadow: 0 2px 8px rgba(45,50,80,0.10);
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          color: var(--muted);
          white-space: nowrap;
        }
        .jota-chat-limit-pill.blocked {
          color: var(--ink-soft);
        }
        /* Banner cinza que substitui o input quando o limite do dia é atingido */
        .jota-limit-banner {
          margin: 0 14px calc(14px + env(safe-area-inset-bottom, 0px));
          padding: 16px 18px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 18px;
          text-align: center;
        }
        .jota-limit-banner-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-limit-banner-text {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          line-height: 1.5;
          color: var(--ink-soft);
          margin: 6px 0 14px;
        }
        .jota-limit-banner-btn {
          width: 100%;
          padding: 12px;
          border: none;
          border-radius: 100px;
          background: var(--ink);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 700;
          cursor: pointer;
        }
        .jota-chat-welcome {
          flex: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          gap: 22px;
          padding: 0 28px calc(96px + env(safe-area-inset-bottom, 0px));
          text-align: center;
        }
        .jota-chat-welcome-title {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 4px;
          max-width: 320px;
        }
        .jota-chat-welcome-hi {
          font-family: 'Open Sans', sans-serif;
          font-size: 22px;
          font-weight: 800;
          line-height: 1.25;
          color: var(--ink);
        }
        .jota-chat-welcome-sub {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          line-height: 1.3;
          color: var(--muted);
        }
        .jota-chat-welcome-q {
          font-family: 'Open Sans', sans-serif;
          font-size: 16px;
          font-weight: 700;
          line-height: 1.3;
          color: var(--ink);
          margin-top: 6px;
        }
        .jota-chat-suggestions {
          display: flex;
          flex-direction: column;
          align-items: stretch;
          gap: 9px;
          width: 100%;
          max-width: 320px;
          /* mais respiro entre o texto de saudação e os botões de sugestão */
          margin-top: 18px;
        }
        .jota-chat-suggestion {
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 11px 17px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
          text-align: left;
          line-height: 1.3;
        }
        .jota-chat-suggestion:disabled { opacity: 0.5; cursor: default; }
        @keyframes jota-sugg-in {
          from { opacity: 0; transform: translateY(2px); }
          to { opacity: 1; transform: none; }
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-chat-suggestion { animation: none; }
        }
        .jota-thinking-row {
          align-self: flex-start;
          padding-left: 2px;
        }
        /* Indicador "digitando" — 3 pontinhos numa bolha, no lugar do avatar */
        .jota-typing {
          display: inline-flex;
          align-items: center;
          gap: 5px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 12px 16px;
        }
        .jota-typing span {
          width: 7px;
          height: 7px;
          border-radius: 50%;
          background: var(--muted);
          animation: jota-typing-bounce 1.3s ease-in-out infinite;
        }
        .jota-typing span:nth-child(2) { animation-delay: 0.18s; }
        .jota-typing span:nth-child(3) { animation-delay: 0.36s; }
        @keyframes jota-typing-bounce {
          0%, 60%, 100% { transform: translateY(0); opacity: 0.45; }
          30% { transform: translateY(-4px); opacity: 1; }
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-typing span { animation: none; }
        }
        .jota-messages {
          flex: 1;
          overflow-y: auto;
          /* base folgada pro fio rolar por trás da barra de vidro */
          padding: 64px 18px calc(88px + env(safe-area-inset-bottom, 0px));
          display: flex;
          flex-direction: column;
          gap: 12px;
        }
        /* Empurra as mensagens pra base quando há poucas — assim a coluna de
           botões flutuante no topo fica sobre espaço vazio, sem cobrir bolha.
           margin-top:auto colapsa quando o fio enche (scroll normal, topo ok). */
        .jota-messages-spacer {
          margin-top: auto;
        }
        .jota-voice-mode {
          flex: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          gap: 20px;
          position: relative;
        }
        .jota-voice-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-voice-transcript {
          max-width: 300px;
          padding: 0 20px;
          font-family: 'Open Sans', sans-serif;
          font-size: 15px;
          font-weight: 600;
          line-height: 1.5;
          color: var(--ink);
          text-align: center;
        }
        .jota-voice-stop {
          position: absolute;
          bottom: 32px;
          width: 48px;
          height: 48px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        .jota-message-row {
          display: flex;
          align-items: flex-end;
          gap: 7px;
          max-width: 85%;
        }
        .jota-message-row.assistant {
          align-self: flex-start;
        }
        .jota-message-row.user {
          align-self: flex-end;
          flex-direction: row-reverse;
        }
        .jota-message-col {
          display: flex;
          flex-direction: column;
          gap: 5px;
          min-width: 0;
        }
        .jota-action-badges {
          display: flex;
          flex-direction: column;
          gap: 4px;
        }
        .jota-action-badge {
          display: inline-flex;
          align-items: center;
          gap: 5px;
          align-self: flex-start;
          background: var(--ink);
          color: #fff;
          border-radius: 100px;
          padding: 5px 11px;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
        }
        .jota-msg-avatar {
          flex-shrink: 0;
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-msg-avatar-user {
          width: 26px;
          height: 26px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          color: var(--muted);
        }
        .jota-bubble {
          padding: 11px 15px;
          border-radius: 18px;
          font-size: 14px;
          line-height: 1.45;
          min-width: 0;
          white-space: pre-wrap;
        }
        .jota-bubble.assistant {
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink);
          border-bottom-left-radius: 5px;
        }
        .jota-bubble.user {
          background: var(--ink);
          color: #fff;
          border-bottom-right-radius: 5px;
          font-weight: 500;
        }
        .jota-chat-options {
          display: flex;
          flex-direction: column;
          gap: 6px;
          margin-top: 8px;
        }
        .jota-chat-option-btn {
          text-align: left;
          background: var(--paper);
          border: 1.5px solid var(--hairline-strong);
          border-radius: 14px;
          padding: 9px 13px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-chat-option-btn:hover {
          border-color: var(--ink);
        }
        .jota-chat-option-btn:disabled {
          opacity: 0.5;
          cursor: default;
        }
        .jota-speak-btn {
          display: inline-flex;
          align-items: center;
          justify-content: center;
          background: none;
          border: none;
          color: var(--muted);
          cursor: pointer;
          padding: 2px;
          margin-left: 6px;
          vertical-align: middle;
        }
        .jota-speak-btn.active {
          color: var(--ink);
        }
        .jota-typing {
          align-self: flex-start;
          display: flex;
          gap: 4px;
          padding: 13px 16px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 18px;
          border-bottom-left-radius: 5px;
        }
        .jota-typing span {
          width: 6px;
          height: 6px;
          border-radius: 50%;
          background: var(--coral);
          animation: jota-typing 1.2s ease-in-out infinite;
        }
        .jota-typing span:nth-child(2) { animation-delay: 0.15s; }
        .jota-typing span:nth-child(3) { animation-delay: 0.3s; }
        @keyframes jota-typing {
          0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
          30% { opacity: 1; transform: translateY(-3px); }
        }
        .jota-input-row {
          position: absolute;
          bottom: 0;
          left: 0;
          right: 0;
          z-index: 5;
          display: flex;
          align-items: center;
          gap: 10px;
          /* sobe do home indicator do iPhone (safe-area) pra não cortar os botões */
          padding: 14px 16px calc(16px + env(safe-area-inset-bottom, 0px));
          /* sem linha cinza: fio branco sutil + sombra pra cima (padrão do app) */
          border-top: 1px solid rgba(255,255,255,0.55);
          box-shadow: 0 -8px 24px rgba(40,45,70,0.07);
          /* vidro + cantos superiores arredondados (o fio de mensagens rola atrás) */
          background: rgba(255,255,255,0.55);
          -webkit-backdrop-filter: blur(22px) saturate(180%);
          backdrop-filter: blur(22px) saturate(180%);
          border-top-left-radius: 32px;
          border-top-right-radius: 32px;
        }
        .jota-mic-btn {
          width: 38px;
          height: 38px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
        }
        .jota-input {
          flex: 1;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 12px 16px;
          color: var(--ink);
          font-size: 14px;
          font-family: 'Open Sans', sans-serif;
        }
        .jota-input:focus {
          outline: 1.5px solid var(--ink);
          outline-offset: 1px;
        }
        .jota-send {
          width: 40px;
          height: 40px;
          border-radius: 50%;
          background: var(--ink);
          border: none;
          color: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          flex-shrink: 0;
        }
        .jota-send:disabled {
          opacity: 0.4;
          cursor: default;
        }

        /* Bloco de calendário embutido na tela de Rotina (RoutineCalendarBlock).
           Mesma linguagem visual de .jota-card. */
        .jota-rotina-cal-card {
          background: var(--paper);
          border: 1px solid var(--hairline);
          border-radius: 20px;
          padding: 18px 18px 4px;
          margin-bottom: 14px;
        }
        .jota-cal-toggle {
          display: flex;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 3px;
          margin-bottom: 16px;
        }
        .jota-cal-toggle-btn {
          flex: 1;
          background: none;
          border: none;
          border-radius: 9px;
          padding: 8px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--muted);
          cursor: pointer;
        }
        .jota-cal-toggle-btn.active {
          background: var(--paper);
          color: var(--ink);
          box-shadow: 0 1px 3px rgba(20,20,24,0.1);
        }
        .jota-cal-nav {
          display: flex;
          align-items: center;
          justify-content: space-between;
          margin-bottom: 14px;
        }
        .jota-cal-period {
          font-family: 'Open Sans', sans-serif;
          font-weight: 700;
          font-size: 14.5px;
          color: var(--ink);
        }
        .jota-cal-week-row {
          display: flex;
          justify-content: space-between;
          margin-bottom: 18px;
        }
        .jota-cal-day {
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 6px;
          background: none;
          border: none;
          padding: 8px 4px;
          border-radius: 12px;
          cursor: pointer;
          width: 13.5%;
        }
        .jota-cal-day-wd {
          font-family: 'Open Sans', sans-serif;
          font-size: 10px;
          font-weight: 600;
          text-transform: uppercase;
          color: var(--muted);
        }
        .jota-cal-day-num {
          font-family: 'Open Sans', sans-serif;
          font-size: 15px;
          font-weight: 600;
          color: var(--ink);
          width: 30px;
          height: 30px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-cal-day.today .jota-cal-day-num {
          border: 1.5px solid var(--ink);
        }
        .jota-cal-day.selected .jota-cal-day-num {
          background: var(--ink);
          color: #fff;
        }
        .jota-cal-dot {
          width: 4px;
          height: 4px;
          border-radius: 50%;
          background: var(--muted);
          margin-top: -2px;
        }
        .jota-cal-day.selected .jota-cal-dot {
          background: var(--ink);
        }
        .jota-cal-month {
          margin-bottom: 18px;
        }
        .jota-cal-month-head {
          display: flex;
          margin-bottom: 6px;
        }
        .jota-cal-month-head span {
          width: 14.28%;
          text-align: center;
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
          color: var(--muted);
        }
        .jota-cal-month-row {
          display: flex;
        }
        .jota-cal-cell {
          width: 14.28%;
          aspect-ratio: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          gap: 3px;
          background: none;
          border: none;
          cursor: pointer;
        }
        .jota-cal-cell-num {
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 500;
          color: var(--ink-soft);
          width: 28px;
          height: 28px;
          border-radius: 50%;
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-cal-cell.muted .jota-cal-cell-num {
          color: var(--muted);
          opacity: 0.4;
        }
        .jota-cal-cell.today .jota-cal-cell-num {
          border: 1.5px solid var(--ink);
        }
        .jota-cal-cell.selected .jota-cal-cell-num {
          background: var(--ink);
          color: #fff;
        }
        .jota-cal-cell .jota-cal-dot {
          margin-top: 0;
        }
        .jota-cal-cell.selected .jota-cal-dot {
          background: var(--ink);
        }
        .jota-cal-readonly-hint {
          display: block;
          font-weight: 500;
          font-size: 11.5px;
          color: var(--muted);
          margin-top: 2px;
        }

        .jota-notif-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 13;
        }
        .jota-notif-sheet.open {
          transform: translateY(0);
        }
        .jota-chathist-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 15;
        }
        .jota-chathist-sheet.open {
          transform: translateY(0);
        }
        .jota-chathist-new {
          display: flex;
          align-items: center;
          gap: 8px;
          width: 100%;
          background: var(--fog);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 11px 16px;
          margin-bottom: 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-notif-body {
          flex: 1;
          overflow-y: auto;
          padding: 23px 20px 28px;
        }
        .jota-notif-markall {
          display: block;
          margin: 0 0 12px auto;
          background: none;
          border: none;
          color: var(--ink-soft);
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 600;
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-notif-markall:disabled {
          color: var(--muted);
          text-decoration: none;
          cursor: default;
        }
        .jota-notif-item {
          display: flex;
          align-items: flex-start;
          gap: 10px;
          width: 100%;
          background: none;
          border: none;
          border-top: 1px solid var(--hairline);
          padding: 13px 0;
          text-align: left;
          cursor: pointer;
        }
        .jota-notif-item:first-child {
          border-top: none;
        }
        .jota-notif-dot {
          width: 8px;
          height: 8px;
          border-radius: 50%;
          background: var(--ink);
          margin-top: 5px;
          flex-shrink: 0;
        }
        .jota-notif-item.read .jota-notif-dot {
          background: var(--hairline-strong);
        }
        .jota-notif-text {
          display: flex;
          flex-direction: column;
          gap: 2px;
        }
        .jota-notif-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-notif-item.read .jota-notif-title {
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-notif-message {
          font-size: 13px;
          color: var(--ink-soft);
          line-height: 1.4;
        }
        .jota-notif-item.read .jota-notif-message {
          color: var(--muted);
        }
        .jota-notif-date {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
          margin-top: 2px;
        }

        .jota-points-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 13;
        }
        .jota-points-sheet.open {
          transform: translateY(0);
        }
        .jota-points-body {
          flex: 1;
          overflow-y: auto;
          padding: 23px 20px 28px;
        }
        /* Banner de Atons — número solto (sem anel), mesma identidade do céu/orbe */
        .jota-atons-banner {
          position: relative;
          height: 92px;
          border-radius: 20px;
          overflow: hidden;
          isolation: isolate;
          display: flex;
          align-items: center;
          padding: 0 20px;
          margin-bottom: 20px;
          background: linear-gradient(115deg, #2A1652 0%, #4C2F94 52%, #1B1440 100%);
          /* vidro: borda superior iluminada + anel + profundidade */
          box-shadow: inset 0 1px 1px rgba(255,255,255,0.4),
                      inset 0 0 0 1px rgba(255,255,255,0.12),
                      inset 0 -12px 24px rgba(0,0,0,0.1);
        }
        .jota-atons-banner::after {
          content: '';
          position: absolute;
          top: 0; left: 0; right: 0;
          height: 46%;
          z-index: 2;
          pointer-events: none;
          background: linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.05) 42%, transparent 100%);
        }
        .jota-atons-haze {
          position: absolute;
          inset: 0;
          z-index: 1;
          mix-blend-mode: screen;
          opacity: 0.6;
          pointer-events: none;
          background:
            radial-gradient(150px 110px at 20% 30%, rgba(160,90,255,0.55), transparent 70%),
            radial-gradient(150px 110px at 82% 80%, rgba(35,215,235,0.3), transparent 70%);
        }
        .jota-atons-star {
          position: absolute;
          z-index: 1;
          width: 2px;
          height: 2px;
          border-radius: 50%;
          background: #fff;
          box-shadow: 0 0 4px 1px rgba(255,255,255,0.6);
          animation: jota-atons-twinkle 6s ease-in-out infinite;
        }
        @keyframes jota-atons-twinkle { 0%, 100% { opacity: 0; transform: scale(0.5); } 50% { opacity: 1; transform: scale(1.1); } }
        .jota-atons-num-wrap { position: relative; z-index: 3; margin-left: 8px; display: flex; align-items: baseline; gap: 7px; flex-shrink: 0; }
        .jota-atons-num-anchor { position: relative; display: inline-block; }
        .jota-atons-glow {
          position: absolute;
          inset: -70%;
          border-radius: 50%;
          z-index: -1;
          background: radial-gradient(circle, rgba(160,90,255,0.55), transparent 68%);
          filter: blur(9px);
        }
        .jota-atons-num {
          position: relative;
          font-family: 'Open Sans', sans-serif;
          font-weight: 800;
          font-size: 34px;
          color: #fff;
          letter-spacing: -0.02em;
          text-shadow: 0 1px 10px rgba(0,0,0,0.35);
          font-variant-numeric: tabular-nums;
        }
        .jota-atons-orbit { position: absolute; inset: -13px; animation: jota-sky-sail-spin 8s linear infinite; }
        @keyframes jota-sky-sail-spin { to { transform: rotate(360deg); } }
        .jota-atons-dot {
          position: absolute;
          top: 50%;
          left: 50%;
          width: 5px;
          height: 5px;
          border-radius: 50%;
          background: #fff;
          box-shadow: 0 0 6px 2px rgba(120,210,255,0.7);
          transform: translate(-50%, -50%) translateX(27px);
        }
        .jota-atons-unit { font-family: 'Open Sans', sans-serif; font-size: 14px; font-weight: 700; color: rgba(255,255,255,0.72); }
        .jota-atons-txt { position: absolute; right: 20px; top: 50%; transform: translateY(-50%); z-index: 4; text-align: right; max-width: 52%; }
        .jota-atons-msg {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 700;
          color: #fff;
          line-height: 1.32;
          text-shadow: 0 1px 6px rgba(0,0,0,0.3);
        }
        .jota-atons-target {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 600;
          color: rgba(255,255,255,0.6);
          margin-top: 5px;
        }
        @media (prefers-reduced-motion: reduce) {
          .jota-atons-star, .jota-atons-orbit { animation: none; }
          .jota-atons-star { opacity: 0.6; }
        }
        .jota-points-values-link {
          display: block;
          margin: 10px 0 20px;
          background: none;
          border: none;
          padding: 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--muted);
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-points-section-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.05em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 10px;
        }
        .jota-points-week-wrap {
          margin-bottom: 24px;
        }
        .jota-points-month-row {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 8px;
          flex-wrap: wrap;
          margin-bottom: 8px;
        }
        .jota-points-month-select {
          display: flex;
          gap: 6px;
          overflow-x: auto;
          scrollbar-width: none;
        }
        .jota-points-month-select::-webkit-scrollbar {
          display: none;
        }
        .jota-points-month-total {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          color: var(--muted);
          margin-bottom: 10px;
        }
        .jota-points-history-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.05em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 4px;
        }
        .jota-points-entry {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 10px;
          padding: 13px 0;
          border-top: 1px solid var(--hairline);
        }
        .jota-points-entry:first-of-type {
          border-top: none;
        }
        .jota-points-entry-text {
          display: flex;
          flex-direction: column;
          gap: 2px;
        }
        .jota-points-entry-desc {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink);
        }
        .jota-points-entry-date {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
        }
        .jota-points-entry-amount {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
          flex-shrink: 0;
        }

        .jota-atons-values-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 16;
        }
        .jota-atons-values-sheet.open {
          transform: translateY(0);
        }
        .jota-atons-values-intro {
          font-size: 12.5px;
          line-height: 1.5;
          color: var(--muted);
          margin: 0 0 16px;
        }
        .jota-atons-value-row {
          display: flex;
          align-items: center;
          gap: 12px;
          padding: 13px 0;
          border-top: 1px solid var(--hairline);
        }
        .jota-atons-value-row:first-of-type {
          border-top: none;
        }
        .jota-atons-value-icon {
          width: 34px;
          height: 34px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          display: flex;
          align-items: center;
          justify-content: center;
          color: var(--ink);
          flex-shrink: 0;
        }
        .jota-atons-value-text {
          display: flex;
          flex-direction: column;
          gap: 2px;
          flex: 1;
          min-width: 0;
        }
        .jota-atons-value-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 700;
          color: var(--ink);
        }
        .jota-atons-value-desc {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          color: var(--muted);
          line-height: 1.4;
        }
        .jota-atons-value-amount {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
          flex-shrink: 0;
        }

        .jota-rate-sheet {
          position: absolute;
          inset: 0;
          background: var(--paper);
          display: flex;
          flex-direction: column;
          transform: translateY(100%);
          transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
          z-index: 14;
        }
        .jota-rate-sheet.open {
          transform: translateY(0);
        }
        .jota-rate-progress {
          display: flex;
          gap: 4px;
          padding: 0 20px 10px;
        }
        .jota-rate-progress-dot {
          flex: 1;
          height: 3px;
          border-radius: 100px;
          background: var(--hairline-strong);
        }
        .jota-rate-progress-dot.passed {
          background: var(--ink-soft);
        }
        .jota-rate-progress-dot.active {
          background: var(--ink);
        }
        .jota-rate-scroll {
          flex: 1;
          overflow-y: scroll;
          scroll-snap-type: y mandatory;
          scrollbar-width: none;
          -webkit-overflow-scrolling: touch;
          overscroll-behavior-y: contain;
        }
        .jota-rate-scroll::-webkit-scrollbar {
          display: none;
        }
        .jota-rate-slide {
          position: relative;
          height: 100%;
          scroll-snap-align: start;
          scroll-snap-stop: always;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          padding: 20px 32px 60px;
          text-align: center;
          box-sizing: border-box;
        }
        .jota-rate-photo {
          margin-bottom: 18px;
        }
        .jota-rate-slide.has-used-badge .jota-rate-photo {
          margin-top: 22px;
        }
        .jota-rate-corner-actions {
          position: absolute;
          top: 16px;
          right: 20px;
          display: flex;
          flex-direction: column;
          gap: 8px;
          z-index: 2;
        }
        .jota-rate-name {
          font-family: 'Open Sans', sans-serif;
          font-size: 19px;
          font-weight: 700;
          color: var(--ink);
          margin: 4px 0 6px;
        }
        .jota-rate-meta {
          display: flex;
          align-items: center;
          gap: 4px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
          margin-bottom: 4px;
        }
        .jota-rate-meta svg {
          color: var(--ink);
          flex-shrink: 0;
        }
        .jota-rate-price {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          color: var(--muted);
          margin-bottom: 14px;
        }
        .jota-rate-desc {
          font-size: 13px;
          line-height: 1.55;
          color: var(--ink-soft);
          margin: 0 0 28px;
          max-width: 280px;
        }
        .jota-rate-stars {
          display: flex;
          gap: 14px;
          margin-bottom: 16px;
        }
        .jota-rate-stars button {
          background: none;
          border: none;
          color: var(--ink);
          cursor: pointer;
          padding: 6px;
          transition: transform 0.15s ease;
        }
        .jota-rate-stars button:active {
          transform: scale(1.2);
        }
        .jota-rate-hint {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 500;
          color: var(--muted);
        }
        .jota-rate-scroll-hint {
          position: absolute;
          bottom: 16px;
          left: 50%;
          transform: translateX(-50%);
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 2px;
          color: var(--muted);
          animation: jota-scroll-hint-bounce 1.6s ease-in-out infinite;
        }
        .jota-rate-scroll-hint span {
          font-family: 'Open Sans', sans-serif;
          font-size: 10.5px;
          font-weight: 600;
        }
        @keyframes jota-scroll-hint-bounce {
          0%, 100% { transform: translateX(-50%) translateY(0); }
          50% { transform: translateX(-50%) translateY(5px); }
        }
        .jota-rate-done-slide {
          padding-left: 40px;
          padding-right: 40px;
        }
        .jota-rate-done-badge {
          width: 64px;
          height: 64px;
          border-radius: 50%;
          background: var(--ink);
          color: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
          margin-bottom: 18px;
        }
        .jota-rate-done-close {
          width: auto;
          padding: 12px 28px;
          margin-top: 8px;
        }
        .jota-rate-used-badge {
          position: absolute;
          top: 16px;
          left: 50%;
          transform: translateX(-50%);
          background: var(--ink);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 10px;
          font-weight: 700;
          letter-spacing: 0.03em;
          padding: 5px 12px;
          border-radius: 100px;
        }
        .jota-rate-choice-label {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.04em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 8px;
        }
        .jota-rate-later-btn {
          margin-top: 10px;
          background: var(--paper);
          border: 1.5px solid var(--ink);
          border-radius: 100px;
          padding: 12px 20px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 700;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-honesty-overlay {
          position: absolute;
          inset: 0;
          background: rgba(20,20,24,0.45);
          display: flex;
          align-items: center;
          justify-content: center;
          padding: 28px;
          z-index: 40;
        }
        .jota-honesty-card {
          background: var(--paper);
          border-radius: 20px;
          padding: 26px 22px;
          display: flex;
          flex-direction: column;
          align-items: center;
          text-align: center;
          gap: 4px;
          width: 100%;
        }
        .jota-honesty-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 16px;
          font-weight: 700;
          color: var(--ink);
          margin-bottom: 6px;
        }
        .jota-honesty-text {
          font-size: 13px;
          line-height: 1.55;
          color: var(--ink-soft);
          margin: 0 0 18px;
        }
        /* Variante do disclaimer completo: texto corrido alinhado à esquerda,
           rolagem interna e botão sempre visível no rodapé do card. */
        .jota-honesty-card.disclaimer {
          align-items: stretch;
          text-align: left;
          gap: 0;
          max-height: 100%;
          padding: 24px 20px 18px;
          box-sizing: border-box;
        }
        .jota-honesty-card.disclaimer .jota-honesty-title {
          text-align: center;
          margin-bottom: 12px;
        }
        .jota-honesty-scroll {
          overflow-y: auto;
          min-height: 0;
          margin-bottom: 16px;
          padding-right: 6px;
        }
        .jota-honesty-scroll .jota-honesty-text {
          text-align: left;
          margin: 0 0 12px;
        }
        .jota-honesty-scroll .jota-honesty-text:last-child {
          margin-bottom: 0;
        }
        .jota-honesty-secondary {
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          cursor: pointer;
          padding: 10px 0 0;
        }
        .jota-rate-toast {
          position: absolute;
          bottom: 36px;
          left: 50%;
          transform: translateX(-50%);
          background: var(--ink);
          color: #fff;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 700;
          padding: 10px 20px;
          border-radius: 100px;
          animation: jota-toast-pop 1.4s ease-out;
          pointer-events: none;
        }
        @keyframes jota-toast-pop {
          0% { opacity: 0; transform: translateX(-50%) translateY(8px); }
          15% { opacity: 1; transform: translateX(-50%) translateY(0); }
          80% { opacity: 1; }
          100% { opacity: 0; transform: translateX(-50%) translateY(-6px); }
        }

        .jota-onboard-overlay {
          position: fixed;
          inset: 0;
          background: var(--paper);
          z-index: 30;
          display: flex;
          flex-direction: column;
        }
        .jota-onboard-screen {
          position: relative;
          flex: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          padding: 40px 28px;
          text-align: center;
          overflow-y: auto;
        }
        .jota-onboard-logo {
          height: 40px;
          margin-bottom: 32px;
        }
        /* ---- Entrada em vidro fosco, tema claro (só a tela de login) ----
           A folha de vidro sobe do rodapé: colada na base da tela, largura
           total, arredondada só em cima e sem base (sem borda embaixo). O orbe
           fica no espaço claro acima. */
        .jota-glass-login {
          position: relative;
          overflow-y: auto;
          justify-content: flex-start;
          gap: 0;
          padding: 0;
          background: radial-gradient(125% 85% at 50% -8%, #FFFFFF 0%, #F5F5F7 55%, #ECECEF 100%);
        }
        .jota-glass-bg {
          position: absolute;
          inset: 0;
          z-index: 0;
          overflow: hidden;
          pointer-events: none;
        }
        .jota-glass-blob {
          position: absolute;
          border-radius: 50%;
          filter: blur(70px);
        }
        .jota-glass-blob-a {
          width: 280px; height: 280px;
          top: -70px; left: -80px;
          background: radial-gradient(circle, rgba(190,196,208,0.30), transparent 70%);
        }
        .jota-glass-blob-b {
          width: 320px; height: 320px;
          top: 34%; right: -90px;
          background: radial-gradient(circle, rgba(205,205,214,0.22), transparent 70%);
        }
        .jota-glass-hero {
          position: relative;
          z-index: 1;
          flex: 1;
          min-height: 150px;
          width: 100%;
          display: flex;
          align-items: center;
          justify-content: center;
        }
        .jota-glass-card {
          position: relative;
          z-index: 1;
          width: 100%;
          box-sizing: border-box;
          display: flex;
          flex-direction: column;
          align-items: center;
          padding: 30px 24px calc(32px + env(safe-area-inset-bottom, 0px));
          border-radius: 28px 28px 0 0;
          background: rgba(255,255,255,0.72);
          border: 1px solid rgba(20,20,24,0.07);
          border-bottom: none;
          box-shadow: 0 -12px 40px rgba(20,20,24,0.09);
          -webkit-backdrop-filter: blur(20px) saturate(140%);
          backdrop-filter: blur(20px) saturate(140%);
        }
        .jota-glass-card .jota-onboard-logo {
          height: 30px;
          margin-bottom: 20px;
        }
        /* Rodapé do card de login: dois atalhos com ícone (estilo BEON) que
           alternam o modo do formulário — Criar conta (pessoa+) e Entrar (digital). */
        .jota-login-footer {
          display: flex;
          align-items: stretch;
          width: 100%;
          max-width: 280px;
          margin-top: 18px;
          padding-top: 16px;
          border-top: 1px solid var(--hairline);
        }
        .jota-login-footer-btn {
          flex: 1;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 6px;
          background: none;
          border: none;
          cursor: pointer;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          font-weight: 700;
          padding: 4px 0;
        }
        .jota-login-footer-btn.active {
          color: var(--ink);
        }
        .jota-login-footer-div {
          width: 1px;
          background: var(--hairline);
          margin: 4px 2px;
        }
        .jota-onboard-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 21px;
          font-weight: 700;
          color: var(--ink);
          margin: 18px 0 10px;
        }
        .jota-onboard-text {
          font-size: 13.5px;
          line-height: 1.6;
          color: var(--ink-soft);
          margin: 0 0 26px;
          max-width: 300px;
        }
        /* Pergunta do onboarding (ex.: "Como você gostaria de ser chamado(a)?") */
        .jota-onboard-q {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--ink);
          margin-bottom: 10px;
        }
        /* Na tela de boas-vindas (sem card), limita a largura do campo de nome */
        .jota-onboard-welcome .jota-onboard-input {
          max-width: 320px;
          margin-bottom: 16px;
        }
        /* Botão de voltar (setinha) no topo do onboarding */
        .jota-onboard-back {
          position: absolute;
          top: calc(14px + env(safe-area-inset-top, 0px));
          left: 14px;
          width: 38px;
          height: 38px;
          display: flex;
          align-items: center;
          justify-content: center;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink);
          cursor: pointer;
          z-index: 2;
        }
        /* Boas-vindas: "Bem-vindo(a)!" menor em cima, "Sou o J-OTA!" grande
           embaixo, com mais respiro entre o avatar e o texto. */
        .jota-onboard-welcome-hi {
          margin-top: 34px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink-soft);
          letter-spacing: 0.2px;
        }
        .jota-onboard-welcome-title {
          margin-top: 4px;
          margin-bottom: 10px;
          font-size: 25px;
        }
        /* ---- Tela de "pensando" pós-análise ---- */
        .jota-thinking-eyebrow {
          margin-top: 34px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink-soft);
          letter-spacing: 0.2px;
        }
        /* altura fixa (2 linhas) pra o % e a régua não pularem quando o título
           vira 1 linha ("Prontinho!") */
        .jota-thinking-title {
          margin: 4px 0 0;
          height: 63px;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          font-family: 'Open Sans', sans-serif;
          font-size: 25px;
          font-weight: 700;
          line-height: 1.25;
          color: var(--ink);
        }
        .jota-thinking-prog {
          margin-top: 26px;
          width: 100%;
          max-width: 240px;
          display: flex;
          flex-direction: column;
          align-items: center;
          gap: 10px;
        }
        .jota-thinking-pct {
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 700;
          color: var(--muted);
          font-variant-numeric: tabular-nums;
        }
        .jota-thinking-bar {
          width: 100%;
          height: 5px;
          border-radius: 100px;
          background: var(--hairline);
          overflow: hidden;
        }
        .jota-thinking-bar-fill {
          height: 100%;
          border-radius: 100px;
          background: var(--ink);
          transition: width 0.15s linear;
        }
        .jota-thinking-thought {
          position: relative;
          margin-top: 24px;
          width: 100%;
          max-width: 300px;
          height: 46px;
          display: flex;
          align-items: flex-start;
          justify-content: center;
        }
        .jota-thinking-phrase {
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 500;
          line-height: 1.5;
          color: var(--muted);
          text-align: center;
          animation: jota-thinking-fade 0.5s ease;
        }
        @keyframes jota-thinking-fade { from { opacity: 0; } to { opacity: 1; } }
        .jota-thinking-cta {
          max-width: 280px;
          margin-top: 8px;
          visibility: hidden;
        }
        .jota-thinking-cta.show { visibility: visible; }
        @media (prefers-reduced-motion: reduce) {
          .jota-thinking-phrase { animation: none; }
          .jota-thinking-bar-fill { transition: none; }
        }
        .jota-onboard-tabs {
          display: flex;
          background: var(--fog);
          border-radius: 100px;
          padding: 3px;
          margin-bottom: 20px;
          width: 100%;
          max-width: 280px;
        }
        .jota-onboard-tabs button {
          flex: 1;
          background: none;
          border: none;
          padding: 9px 0;
          border-radius: 100px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--muted);
          cursor: pointer;
        }
        .jota-onboard-tabs button.active {
          background: var(--ink);
          color: #fff;
        }
        .jota-onboard-input {
          width: 100%;
          max-width: 280px;
          box-sizing: border-box;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 13px 15px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          color: var(--ink);
          outline: none;
          margin-bottom: 10px;
        }
        .jota-onboard-input::placeholder {
          color: var(--muted);
        }
        /* Avisos do login/cadastro (erro, conta criada, etc.): cantos
           arredondados, ícone do tema à esquerda e letra cinza — mesmo padrão
           sóbrio do app. O tipo muda só o ícone; o texto é sempre cinza. */
        .jota-auth-msg {
          display: flex;
          align-items: flex-start;
          gap: 8px;
          width: 100%;
          max-width: 280px;
          box-sizing: border-box;
          border-radius: 16px;
          padding: 11px 14px;
          margin-bottom: 10px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          line-height: 1.4;
          color: var(--ink-soft);
          text-align: left;
        }
        .jota-auth-msg svg {
          flex-shrink: 0;
          margin-top: 1px;
          color: var(--muted);
        }
        /* Avisos informativos/positivos: cinza (padrão acima).
           Avisos negativos (erro): vermelho mais escuro no texto, ícone e borda. */
        .jota-auth-msg-error {
          color: #A32017;
          background: rgba(163,32,23,0.06);
          border-color: rgba(163,32,23,0.22);
        }
        .jota-auth-msg-error svg {
          color: #A32017;
        }
        .jota-onboard-terms {
          display: flex;
          align-items: flex-start;
          gap: 8px;
          width: 100%;
          max-width: 280px;
          margin-bottom: 12px;
          cursor: pointer;
        }
        .jota-onboard-terms input[type="checkbox"] {
          margin-top: 2px;
          width: 16px;
          height: 16px;
          flex-shrink: 0;
          accent-color: var(--ink);
          cursor: pointer;
        }
        .jota-onboard-terms span {
          font-family: 'Open Sans', sans-serif;
          font-size: 12px;
          line-height: 1.45;
          color: var(--ink-soft);
          text-align: left;
        }
        .jota-terms-link {
          background: none;
          border: none;
          padding: 0;
          font: inherit;
          color: var(--ink);
          font-weight: 700;
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-review-submit:disabled,
        .jota-onboard-social:disabled {
          opacity: 0.55;
          cursor: default;
        }
        /* Recuperação de senha: link "esqueci", dica e "voltar ao login" */
        .jota-forgot-link {
          width: 100%;
          max-width: 280px;
          box-sizing: border-box;
          text-align: right;
          background: none;
          border: none;
          padding: 2px 0;
          margin: -4px 0 2px;
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          color: var(--ink-soft);
          text-decoration: underline;
          cursor: pointer;
        }
        .jota-recover-back {
          text-align: center;
          margin-top: 4px;
        }
        .jota-recover-hint {
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 500;
          line-height: 1.4;
          color: var(--muted);
          margin: -2px 0 4px;
          text-align: left;
        }
        .jota-reset-title {
          font-family: 'Open Sans', sans-serif;
          font-size: 16px;
          font-weight: 800;
          color: var(--ink);
          text-align: center;
          margin: 2px 0 2px;
        }
        .jota-profile-name-edit {
          display: flex;
          align-items: center;
          gap: 8px;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 100px;
          padding: 6px 6px 6px 14px;
        }
        .jota-profile-name-edit .jota-add-input {
          padding: 0;
        }
        .jota-profile-name-display {
          display: flex;
          align-items: center;
          justify-content: space-between;
          width: 100%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          border-radius: 12px;
          padding: 11px 14px;
          font-family: 'Open Sans', sans-serif;
          font-size: 14px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
        }
        .jota-profile-name-saved {
          display: block;
          margin-top: 8px;
          font-family: 'Open Sans', sans-serif;
          font-size: 11.5px;
          font-weight: 600;
          color: var(--ink-soft);
        }
        .jota-logout-btn {
          margin-top: 20px;
          width: 100%;
          background: none;
          border: 1px solid var(--hairline-strong);
          border-radius: 12px;
          padding: 13px 15px;
          font-family: 'Open Sans', sans-serif;
          font-size: 13.5px;
          font-weight: 600;
          color: var(--ink-soft);
          cursor: pointer;
        }
        .jota-logout-btn:disabled { opacity: 0.55; cursor: default; }
        .jota-onboard-primary {
          max-width: 280px;
          margin-top: 6px;
        }
        .jota-onboard-divider {
          display: flex;
          align-items: center;
          gap: 10px;
          width: 100%;
          max-width: 280px;
          margin: 18px 0 14px;
        }
        .jota-onboard-divider::before,
        .jota-onboard-divider::after {
          content: '';
          flex: 1;
          height: 1px;
          background: var(--hairline);
        }
        .jota-onboard-divider span {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
        }
        .jota-onboard-social {
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 10px;
          width: 100%;
          max-width: 280px;
          background: var(--paper);
          border: 1px solid var(--hairline-strong);
          border-radius: 100px;
          padding: 11px 0;
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink);
          cursor: pointer;
          margin-bottom: 8px;
        }
        .jota-onboard-secondary {
          background: none;
          border: none;
          color: var(--muted);
          font-family: 'Open Sans', sans-serif;
          font-size: 12.5px;
          font-weight: 600;
          text-decoration: underline;
          cursor: pointer;
          margin-top: 14px;
        }
        .jota-onboard-routine-screen {
          justify-content: flex-start;
          padding-top: 56px;
        }
        .jota-onboard-routine-groups {
          width: 100%;
          max-width: 300px;
          display: flex;
          flex-direction: column;
          gap: 18px;
          margin-bottom: 26px;
        }
        .jota-onboard-routine-group {
          text-align: left;
        }
        .jota-onboard-routine-label {
          display: block;
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          font-weight: 700;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--muted);
          margin-bottom: 8px;
        }
        .jota-onboard-routine-item {
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 8px 0;
          border-top: 1px solid var(--hairline);
        }
        .jota-onboard-routine-item:first-of-type {
          border-top: none;
        }
        .jota-onboard-routine-item-text {
          display: flex;
          flex-direction: column;
          gap: 1px;
        }
        .jota-onboard-routine-item-text .title {
          font-family: 'Open Sans', sans-serif;
          font-size: 13px;
          font-weight: 600;
          color: var(--ink);
        }
        .jota-onboard-routine-item-text .sub {
          font-family: 'Open Sans', sans-serif;
          font-size: 11px;
          color: var(--muted);
        }
        .jota-tour-screen {
          position: relative;
        }
        .jota-tour-skip-bottom {
          margin-top: 12px;
          font-size: 11.5px;
        }
        .jota-tour-icon {
          width: 76px;
          height: 76px;
          border-radius: 50%;
          background: var(--fog);
          border: 1px solid var(--hairline);
          color: var(--ink-soft);
          display: flex;
          align-items: center;
          justify-content: center;
          margin-bottom: 6px;
        }
        .jota-tour-progress {
          max-width: 140px;
          margin-bottom: 28px;
        }

        /* ── MOBILE (fim do CSS de propósito: precisa vencer as regras base) ──
           Tela cheia sem moldura + env(safe-area-inset-*) afasta o conteúdo do
           notch (topo) e da home bar (base) do iPhone/Android. */
        @media (max-width: 767px) {
          .jota-app {
            max-width: 100%;
            /* 100dvh = altura dinâmica do viewport: no iPhone standalone alcança
               o fundo físico da tela (inclui a safe-area da barrinha). É o que
               faz a navbar (absolute; bottom:0) grudar na borda de baixo de
               verdade. NÃO trocar por height:100% (depende de cadeia de
               containers e no iOS pode ficar curto, subindo a navbar). */
            height: 100vh;
            height: 100dvh;
            border-radius: 0;
            box-shadow: none;
            border: none;
            padding-top: env(safe-area-inset-top);
            box-sizing: border-box;
            touch-action: manipulation;
          }
          .jota-navbar {
            /* position:fixed ancora no LAYOUT viewport (não no .jota-app, que
               mede 100dvh). Isso é de propósito: o dvh ENCOLHE quando o teclado
               abre no iOS, e uma navbar absolute;bottom:0 dentro dele subia junto
               (parecia "levantada" ao voltar de um campo de texto). O layout
               viewport NÃO encolhe com o teclado, então fixed;bottom:0 fica
               grudada na borda física de baixo o tempo todo. (.jota-app não tem
               transform/filter, então o fixed ancora no viewport de verdade.)
               A altura inclui a safe-area; o padding-bottom sobe os botões acima
               do home indicator, com o vidro cobrindo a faixa até a barrinha. */
            position: fixed;
            height: calc(78px + env(safe-area-inset-bottom, 0px));
            padding: 0 22px calc(env(safe-area-inset-bottom, 0px) + 4px);
          }
          .jota-body {
            /* corpo ocupa tudo; header e navbar de vidro ficam por cima */
            padding-bottom: calc(78px + 28px + env(safe-area-inset-bottom, 0px));
          }
          /* Sheets/overlays de tela cheia são position:absolute e ignoram o
             padding do container — precisam do próprio respiro do notch. */
          .jota-sheet,
          .jota-notif-sheet,
          .jota-chathist-sheet,
          .jota-points-sheet,
          .jota-atons-values-sheet,
          .jota-product-sheet,
          .jota-rate-sheet,
          .jota-wizard-sheet,
          .jota-diary-post-sheet,
          .jota-onboard-overlay {
            padding-top: env(safe-area-inset-top);
            box-sizing: border-box;
          }
        }

        /* ── Varredura de arredondamento ──────────────────────────────────
           Botões e campos com o canto no máximo (pill). Campos/botões de
           uma linha viram pílula; caixas de opção com várias linhas e áreas
           de texto ganham um arredondado generoso (pra não virar cápsula). */
        .jota-cal-toggle,
        .jota-cal-toggle-btn,
        .jota-item-view,
        .jota-item-action,
        .jota-logout-btn,
        .jota-diary-more-btn,
        .jota-recur-chip,
        .jota-add-routine-btn,
        .jota-date-input,
        .jota-interval-input,
        .jota-onboard-input,
        .jota-recur-time-input,
        .jota-diary-title-input,
        .jota-category-select,
        .jota-profile-name-display {
          border-radius: 100px;
        }
        .jota-chat-option-btn,
        .jota-scope-opt,
        .jota-wizard-option,
        .jota-wizard-select,
        .jota-diary-textarea,
        .jota-review-textarea {
          border-radius: 18px;
        }
      `}</style>

      {!recovering && onboardingStep !== "done" && (
        <div className="jota-onboard-overlay">
          {onboardingStep === "login" && (
            <LoginScreen onAuthed={async () => {
              const { data } = await supabase.auth.getUser();
              setOnboardingStep(data.user?.user_metadata?.onboarded ? "done" : "welcome");
            }} />
          )}
          {onboardingStep === "welcome" && (
            <WelcomeNameScreen
              onBack={() => setOnboardingStep("login")}
              onContinue={(name) => { updateName(name); setOnboardingStep("invite"); }}
            />
          )}
          {onboardingStep === "invite" && (
            <AnalysisInviteScreen
              onBack={() => setOnboardingStep("welcome")}
              onStart={() => setOnboardingStep("quiz")}
              onLater={finishOnboarding}
            />
          )}
          {onboardingStep === "quiz" && (
            <ProfileWizardSheet
              open={true}
              onClose={() => setOnboardingStep("invite")}
              initialAnswers={EMPTY_QUIZ_ANSWERS}
              onComplete={(result) => { saveSkinProfile(result); setOnboardingStep("thinking"); }}
            />
          )}
          {onboardingStep === "thinking" && (
            <AnalysisThinkingScreen
              name={firstName}
              profile={profile}
              onDone={() => setOnboardingStep("suggestion")}
            />
          )}
          {onboardingStep === "suggestion" && (
            <RoutineSuggestionScreen
              profile={profile}
              onAccept={(routines) => { acceptSuggestedRoutines(routines); finishOnboarding(); }}
              onSkip={finishOnboarding}
            />
          )}
        </div>
      )}

      <div className="jota-header">
        <img src={LOGO_SRC} alt="J-OTA — Skin Beauty Technology" className="jota-logo-img" />
        <div className="jota-header-right">
          <button className="jota-header-profile-btn" onClick={() => setTab("perfil")} aria-label="Perfil">
            <span className="jota-header-name">{firstName}</span>
            <span className="jota-header-avatar">
              <User size={14} strokeWidth={1.75} />
            </span>
          </button>
          <button className="jota-notif-btn" onClick={() => setNotificationsOpen(true)} aria-label="Notificações">
            <Bell size={16} strokeWidth={1.75} />
            {unreadCount > 0 && <span className="jota-notif-badge">{unreadCount}</span>}
          </button>
          <button className="jota-points-badge" onClick={() => setPointsSheetOpen(true)}>
            <Circle size={12} strokeWidth={2} />
            <span>{points}</span>
          </button>
        </div>
      </div>

      <div className="jota-body">
        {tab === "rotina" && (
          <RotinaScreen
            routines={routines}
            onToggleStep={toggleStep}
            onAddStep={addStep}
            onRemoveStep={removeStep}
            onMoveStep={moveStep}
            onEditRecurrence={editRecurrence}
            onRenameRoutine={renameRoutine}
            onDeleteRoutine={deleteRoutine}
            onAddRoutine={addRoutine}
            onMoveRoutine={moveRoutine}
            onToggleCategories={toggleRoutineCategories}
            onViewProduct={setRoutineProductView}
            onEditStepInfo={editStepInfo}
          />
        )}
        {tab === "busca" && (
          <BuscaScreen
            onEarnPoints={addPoints}
            routines={routines}
            myRatings={myRatings}
            onRate={rateProduct}
            onSubmitReview={submitReview}
            favorites={favorites}
            toggleFavorite={toggleFavorite}
            wishlist={wishlist}
            toggleWishlist={toggleWishlist}
            necessaire={necessaire}
            productStatus={productStatus}
            tagsFor={tagsFor}
            onOpenNecessaire={() => setNecessaireOpen(true)}
            onOpenTestados={() => setTestadosOpen(true)}
          />
        )}
        {tab === "diario" && (
          <DiarioScreen
            myRatings={myRatings}
            favorites={favorites}
            toggleFavorite={toggleFavorite}
            onRate={rateProduct}
            onSubmitReview={submitReview}
            routines={routines}
            profile={profile}
            setProfile={saveSkinProfile}
            userId={session?.user?.id || null}
            wizardOpen={wizardOpen}
            setWizardOpen={setWizardOpen}
            necessaire={necessaire}
            productStatus={productStatus}
            points={points}
            tagsFor={tagsFor}
            onOpenNecessaire={() => setNecessaireOpen(true)}
            onOpenTestados={() => setTestadosOpen(true)}
            showToast={showToast}
          />
        )}
        {tab === "perfil" && <PerfilScreen userName={userName} userEmail={userEmail} onLogout={logout} onUpdateName={updateName} />}
      </div>

      <div className={`jota-navbar ${keyboardOpen ? "kb-hidden" : ""}`}>
        <div className="jota-navgroup">
          <button className={`jota-navbtn ${tab === "rotina" ? "active" : ""}`} onClick={() => setTab("rotina")}>
            <Check size={20} strokeWidth={1.75} />
            <span>Rotina</span>
          </button>
          <button className={`jota-navbtn ${tab === "busca" ? "active" : ""}`} onClick={() => setTab("busca")}>
            <Search size={20} strokeWidth={1.75} />
            <span>Busca</span>
          </button>
        </div>

        <button className="jota-navcenter" onClick={() => { startNewConversation(); setExpanded(true); }} aria-label="Abrir J-OTA">
          <JotaOrb size={64} />
        </button>

        <div className="jota-navgroup">
          <button className={`jota-navbtn ${tab === "diario" ? "active" : ""}`} onClick={() => setTab("diario")}>
            <BookOpen size={20} strokeWidth={1.75} />
            <span>Diário</span>
          </button>
          <button className="jota-navbtn jota-navbtn-locked" onClick={(e) => e.preventDefault()} aria-label="Em breve — ainda bloqueado">
            <Lock size={18} strokeWidth={1.75} />
            <span>Em breve</span>
          </button>
        </div>
      </div>

      <div className="jota-sheet">
        <div className="jota-chat-head">
          {messages.length > 0 && !voiceMode && (
            <div className="jota-chat-avatar" aria-hidden="true">
              <JotaOrb size={44} />
            </div>
          )}
          <button
            className="jota-chat-close"
            onClick={() => {
              if (window.speechSynthesis) window.speechSynthesis.cancel();
              setSpeakingId(null);
              setExpanded(false);
            }}
            aria-label="Fechar"
          >
            <ChevronLeft size={18} strokeWidth={2} />
          </button>
          <div className="jota-chat-actions">
            {session?.user && (
              dailyCount >= dailyLimit ? (
                <span className="jota-chat-limit-pill blocked" title="Limite diário atingido — reseta à meia-noite">
                  <Clock size={12} strokeWidth={2.2} /> {fmtCountdown(msToBrasilMidnight())}
                </span>
              ) : (
                <span className="jota-chat-limit-pill" title="Mensagens de hoje">{dailyCount}/{dailyLimit}</span>
              )
            )}
            <button className="jota-round-action" onClick={() => setChatDisclaimerOpen(true)} aria-label="Aviso importante">
              <Info size={17} strokeWidth={1.9} />
            </button>
            <button className="jota-round-action" onClick={openChatHistory} aria-label="Conversas anteriores">
              <History size={17} strokeWidth={1.9} />
            </button>
            <button className="jota-round-action" onClick={startNewConversation} aria-label="Nova conversa">
              <SquarePen size={17} strokeWidth={1.9} />
            </button>
          </div>
        </div>

        <DisclaimerModal open={chatDisclaimerOpen} onClose={() => setChatDisclaimerOpen(false)} />

        {voiceMode ? (
          <div className="jota-voice-mode">
            <JotaOrb size={140} speaking />
            <span className="jota-voice-label">{input ? "" : "Ouvindo..."}</span>
            {input && <span className="jota-voice-transcript">{input}</span>}
            <button className="jota-voice-stop" onClick={stopVoice} aria-label="Cancelar">
              <X size={18} strokeWidth={2} />
            </button>
          </div>
        ) : messages.length === 0 ? (
          <div className="jota-chat-welcome">
            <JotaOrb size={88} />
            <div className="jota-chat-welcome-title">
              <span className="jota-chat-welcome-hi">Hello! Sou o J-OTA</span>
              <span className="jota-chat-welcome-sub">Seu Assistente Inteligente de Beleza</span>
              <span className="jota-chat-welcome-q">Como posso te ajudar?</span>
            </div>
            <div className="jota-chat-suggestions">
              {visibleSuggestions.map((s) => (
                <button key={s} className="jota-chat-suggestion" onClick={() => handleSend(s)} disabled={isLoading}>
                  {s}
                </button>
              ))}
            </div>
          </div>
        ) : (
          <div className="jota-messages" ref={scrollRef}>
            <div className="jota-messages-spacer" />
            {messages.map((m, i) => {
              const { text, options } = m.role === "assistant"
                ? splitAssistantContent(m.content)
                : { text: m.content, options: [] };
              return (
                <div key={i} className={`jota-message-row ${m.role}`}>
                  <div className="jota-message-col">
                    {m.actions && m.actions.length > 0 && (
                      <div className="jota-action-badges">
                        {m.actions.map((a, idx) => (
                          <span key={idx} className="jota-action-badge">
                            <Check size={11} strokeWidth={2.5} /> {a}
                          </span>
                        ))}
                      </div>
                    )}
                    <div className={`jota-bubble ${m.role}`}>
                      {m.role === "assistant" ? renderInlineMarkdown(text) : text}
                      {m.role === "assistant" && (
                        <button
                          className={`jota-speak-btn ${speakingId === i ? "active" : ""}`}
                          onClick={() => speakMessage(text, i)}
                          aria-label="Ouvir mensagem"
                        >
                          <Volume2 size={13} strokeWidth={2} />
                        </button>
                      )}
                    </div>
                    {m.role === "assistant" && options.length > 0 && (
                      <div className="jota-chat-options">
                        {options.map((opt, oi) => (
                          <button
                            key={oi}
                            className="jota-chat-option-btn"
                            onClick={() => handleSend(opt)}
                            disabled={isLoading}
                          >
                            {opt}
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
            {isLoading && (
              <div className="jota-thinking-row">
                <div className="jota-typing" aria-label="J-OTA está digitando">
                  <span></span><span></span><span></span>
                </div>
              </div>
            )}
          </div>
        )}

        {dailyCount >= dailyLimit ? (
          <div className="jota-limit-banner">
            <div className="jota-limit-banner-title">Limite de mensagens atingido</div>
            <div className="jota-limit-banner-text">
              Você já usou suas {dailyLimit} mensagens de hoje. Volte amanhã para continuar conversando ou faça o upgrade da sua assinatura.
            </div>
            <button
              className="jota-limit-banner-btn"
              onClick={() => setToast({ message: "Planos e upgrade chegam em breve." })}
            >
              Fazer upgrade!
            </button>
          </div>
        ) : (
          <div className="jota-input-row">
            <button className="jota-mic-btn" onClick={startVoice} aria-label="Falar com o J-OTA">
              <Mic size={17} strokeWidth={1.9} />
            </button>
            <input
              className="jota-input"
              placeholder="Fala com o J-OTA..."
              value={input}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && handleSend()}
            />
            <button className="jota-send" onClick={handleSend} disabled={isLoading || !input.trim()}>
              <Send size={16} />
            </button>
          </div>
        )}
      </div>

      {/* Página do produto aberta a partir do botão "Ver" na rotina */}
      <ProductDetailSheet
        product={routineProductView}
        onClose={() => setRoutineProductView(null)}
        myRating={routineProductView ? myRatings[routineProductView.id] : null}
        onRate={rateProduct}
        onSubmitReview={submitReview}
        isFavorite={routineProductView ? !!favorites[routineProductView.id] : false}
        onToggleFavorite={() => routineProductView && toggleFavorite(routineProductView.id)}
        tags={routineProductView ? tagsFor(routineProductView.id) : null}
      />

      {/* Necessaire e Testados abrem tanto da Busca quanto do Diário — por isso
          vivem aqui na raiz, com estado compartilhado entre as duas telas. */}
      <NecessaireSheet
        open={necessaireOpen}
        onClose={() => setNecessaireOpen(false)}
        necessaire={necessaire}
        productStatus={productStatus}
        routines={routines}
        onOpenProduct={(p) => { setNecessaireOpen(false); setRoutineProductView(p); }}
        onToggleNecessaire={toggleNecessaire}
      />

      <TestadosSheet
        open={testadosOpen}
        onClose={() => setTestadosOpen(false)}
        productStatus={productStatus}
        necessaire={necessaire}
        onOpenProduct={(p) => { setTestadosOpen(false); setRoutineProductView(p); }}
        onSetStatus={setProductStatusTo}
      />

      <NotificationsSheet
        open={notificationsOpen}
        onClose={() => setNotificationsOpen(false)}
        notifications={notifications}
        onToggleRead={toggleNotificationRead}
        onMarkAllRead={markAllNotificationsRead}
      />

      <ChatHistorySheet
        open={chatHistoryOpen}
        onClose={() => setChatHistoryOpen(false)}
        conversations={chatHistoryList}
        onOpenConversation={openConversation}
        onNewConversation={startNewConversation}
      />

      <PointsSheet
        open={pointsSheetOpen}
        onClose={() => setPointsSheetOpen(false)}
        points={points}
        history={pointsHistory}
      />
    </div>
  );
}

// ============================================================
// Bootstrap — monta o app na página (substitui o ambiente de
// artifact do Claude, que fazia isso automaticamente).
// ============================================================
// Handle de depuração no console do navegador (a chave é a publishable,
// que já é pública — os dados continuam protegidos pelo RLS).
window.jotaDebug = { supabase };

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