// V2 — "Studio" — multi-view: home / product / about

const I18N = {
  en: {
    catalog: "Catalog", notes: "Notes", reviews: "Reviews", tools: "Tools", about: "About", contact: "Contact",
    tagline: "Independent · Studio",
    footerTag: "Small books, three languages, one catalog.",
    statusPill: "New title every week · Updated May 2026",
    heroTitle1: "Small books for the", heroTitle2: "quiet operator.",
    heroSub: "A growing catalog of practical handbooks on AI, work, and the small systems that compound. Written across English, Spanish, and Portuguese — published independently, priced to be useful.",
    browseCatalog: "Browse the catalog →", aboutWork: "About the work",
    titlesInPrint: "Titles in print", languages: "Languages", markets: "Markets", priceFloor: "Price floor", lastUpdate: "Last update",
    featured: "Currently featured", featuredTitle: "Three reads, picked this month.", edition: "2026 EDITION",
    readMore: "Read more →", viewArrow: "View →",
    theCatalog: "The catalog", everythingSorted: "Everything, sorted.",
    titles: "titles", titlesIn: "titles in",
    topic: "Topic", market: "Market", allMarkets: "All markets",
    buyArrow: "Buy →", oneTime: "One-time. Lifetime access.", yoursToday: "Yours today.",
    instantDelivery: "Instant delivery. 7-day refund. Free updates for 12 months.",
    whatsInside: "What's inside", shortChapters: "short chapters.", oneClrPath: "One clear path.",
    forYouIf: "This is for you if…", notForYou: "Probably not for you if…",
    forYou1: "and want a ready-to-use playbook, not a theory.",
    forYou2: "You'd rather read once and apply than scroll forever.",
    forYou3: "You believe small, compounding moves beat hype.",
    notForYou1: "You want a 500-page exhaustive treatise.",
    notForYou2: "You're looking for academic citations more than working steps.",
    notForYou3: "You expect personal coaching included.",
    readNext: "Read next", fromSameShelf: "From the same shelf.",
    format: "Format", length: "Length", language: "Language", updates: "Updates", refunds: "Refunds", delivery: "Delivery",
    formatVal: "PDF · ePub · Notion duplicate", lengthVal: "≈ 90–140 pages",
    langEnglish: "English", langEspanol: "Español", langPortugues: "Português",
    updatesVal: "Free for 12 months", refundsVal: "7-day, no questions", deliveryVal: "Instant download",
    notesHeroTitle1: "Short essays from the", notesHeroTitle2: "writing room.",
    notesHeroLede: "A monthly-ish journal about the work — what I'm publishing, what I'm cutting, and what I learned shipping books one at a time.",
    readTheNote: "Read the note →", readSuffix: "read",
    byAuthor: "By Alvaro Abreu", previous: "← Previous", next: "Next →",
    reviewsHeroTitle1: "Tools and books I actually", reviewsHeroTitle2: "pay for and use.",
    reviewsHeroLede: "Working reviews of the software, services, and books that earn a place in my own catalog. No screenshots-only takes — only things I've shipped real work with.",
    affDisclosure: "Affiliate disclosure.", affDisclosureText: "Some links earn me a small commission at no extra cost to you. I don't review what I don't use, and I never accept paid placements.",
    affDisclosureShort: "Some links earn me a small commission at no extra cost to you. I only review what I use.",
    readTheReview: "Read the review →", theVerdict: "The verdict",
    toolsHeroTitle1: "Proprietary tools for", toolsHeroTitle2: "specific problems.",
    toolsHeroLede: "Small, focused utilities built by the studio — calculators, decision frameworks, and micro-apps designed to solve one problem well for one specific professional. No subscriptions. No bloat. Just practical tools you use once and get value from immediately.",
    workshopOpen: "The workshop is open.",
    toolsBody1: "The first tools are currently in development. Think: tax deduction calculators for specific jurisdictions, pricing simulators for indie publishers, compliance checklists that actually check things, and workflow builders that save ten hours a week.",
    toolsBody2: "Each tool is built the same way as the books — for one specific reader, solving one specific problem, priced to be useful rather than impressive. Some will be free. Some will cost the price of a coffee. None will require a monthly subscription.",
    whatToExpect: "What to expect",
    calcTitle: "Calculators & simulators", calcDesc: "Tax estimators, pricing models, ROI calculators — tools that turn guesswork into numbers for specific professions and markets.",
    checkTitle: "Interactive checklists", checkDesc: "Compliance checklists, launch rundowns, and audit frameworks that track your progress and flag what you missed.",
    workflowTitle: "Workflow builders", workflowDesc: "Step-by-step generators for repetitive professional tasks — from contract drafting to content planning to financial reporting.",
    decisionTitle: "Decision frameworks", decisionDesc: "Structured tools that help you think through complex decisions — platform comparisons, hiring vs. automating, market entry analysis.",
    notifyLaunch: "Get notified when tools launch", firstTools: "First tools ship Q3 2026.",
    notifyBody: "Subscribe to the monthly note and you will be the first to know when each tool goes live. No spam — just one email on the first Sunday of the month.",
    notifyMe: "Notify me →",
    apName: "AnesthesiaPro", apTagline: "Precision at your fingertips.",
    apBlurb: "Clinical decision-support calculator for anesthesiologists and residents. Fluid management, drug dosing, ventilation parameters, pediatric adjustments, regional blocks — all from a single patient profile.",
    apFeat1: "Fluids & blood loss", apFeat1d: "4-2-1 maintenance, fasting deficit, estimated blood volume, and allowable blood loss with real-time recommendations.",
    apFeat2: "Drug dosing", apFeat2d: "Weight-based calculations for induction agents, muscle relaxants, vasopressors, and emergency drugs.",
    apFeat3: "Ventilation & pediatrics", apFeat3d: "Tidal volume, respiratory rate, ventilator settings, plus age-based pediatric equipment sizing and doses.",
    apFeat4: "Blocks & techniques", apFeat4d: "Regional anesthesia reference, infusion calculators, and a personal practice log — all in one interface.",
    apBadge: "Free · Web App", apCta: "Open AnesthesiaPro →", apLang: "Available in PT · EN · ES", apTimer: "Built-in surgical timer with phase tracking",
    notifyLaunch2: "Stay in the loop", nextTwoShip: "New tools drop without warning.",
    contactHeroTitle1: "Reach out, and", contactHeroTitle2: "I'll write back.",
    contactHeroLede: "The studio is one person. I read every message and reply within two business days — usually faster.",
    sendMessage: "Send a message", tellMe: "Tell me what you're working on.",
    yourName: "Your name", email: "Email", topicLabel: "Topic", message: "Message",
    topicQ: "A question about a book", topicWrite: "A topic I should write about", topicPartner: "Tool or partnership", topicPress: "Press or interview", topicOther: "Something else",
    sendBtn: "Send message →", formFine: "By sending, you agree to a one-time reply. Your email isn't added to any list.",
    direct: "Direct", replyWithin: "Reply within", timezone: "Timezone", elsewhere: "Elsewhere",
    wontReply: "What I won't reply to", coldPitch: "Cold sales pitches", seoLink: "SEO link exchanges", quickCall: "\"Quick calls\" without an agenda",
    aboutHeroTitle1: "About the studio", aboutHeroTitle2: "Alvaro Abreu",
    aboutHeroLede: "I write short, practical books for people who'd rather adopt a tool well than chase the next one. The catalog is small on purpose, runs in three languages, and grows by one or two titles a week.",
    whyExist: "Why these books exist", howBuilt: "How the catalog is built", whatWontDo: "What I won't do",
    studioNumbers: "The studio, in numbers", person: "person",
    based: "Based", writingSince: "Writing since", distribution: "Distribution", schedule: "Schedule",
    closerTitle1: "If any of this resonates,", closerTitle2: "start with one book.",
    youre: "You're",
    all: "All",
    successKicker: "PAYMENT CONFIRMED",
    successTitle: "You're in. The book is yours.",
    successSub: "Check your email — your download link and receipt are on the way. If you don't see it in a few minutes, check your spam folder.",
    successDelivery: "Your purchase includes instant PDF + ePub download, free updates for 12 months, and a 7-day no-questions refund guarantee.",
    successBrowse: "Browse the catalog →",
    successRelated: "You might also like these.",
  },
  pt: {
    catalog: "Catálogo", notes: "Notas", reviews: "Análises", tools: "Ferramentas", about: "Sobre", contact: "Contato",
    tagline: "Independente · Estúdio",
    footerTag: "Livros curtos, três idiomas, um catálogo.",
    statusPill: "Novo título toda semana · Atualizado maio 2026",
    heroTitle1: "Livros curtos para o", heroTitle2: "operador silencioso.",
    heroSub: "Um catálogo crescente de manuais práticos sobre IA, trabalho e os pequenos sistemas que se acumulam. Escrito em inglês, espanhol e português — publicado de forma independente, com preço justo.",
    browseCatalog: "Ver o catálogo →", aboutWork: "Sobre o trabalho",
    titlesInPrint: "Títulos publicados", languages: "Idiomas", markets: "Mercados", priceFloor: "Preço mínimo", lastUpdate: "Última atualização",
    featured: "Em destaque", featuredTitle: "Três leituras, escolhidas este mês.", edition: "EDIÇÃO 2026",
    readMore: "Ler mais →", viewArrow: "Ver →",
    theCatalog: "O catálogo", everythingSorted: "Tudo, organizado.",
    titles: "títulos", titlesIn: "títulos em",
    topic: "Tema", market: "Mercado", allMarkets: "Todos os mercados",
    buyArrow: "Comprar →", oneTime: "Pagamento único. Acesso vitalício.", yoursToday: "Seu hoje.",
    instantDelivery: "Entrega imediata. Reembolso em 7 dias. Atualizações grátis por 12 meses.",
    whatsInside: "O que tem dentro", shortChapters: "capítulos curtos.", oneClrPath: "Um caminho claro.",
    forYouIf: "Isso é para você se…", notForYou: "Provavelmente não é para você se…",
    forYou1: "e quer um manual prático, não uma teoria.",
    forYou2: "Prefere ler uma vez e aplicar do que rolar infinitamente.",
    forYou3: "Acredita que pequenos passos compostos vencem o hype.",
    notForYou1: "Quer um tratado exaustivo de 500 páginas.",
    notForYou2: "Busca citações acadêmicas em vez de passos práticos.",
    notForYou3: "Espera coaching pessoal incluído.",
    readNext: "Leia a seguir", fromSameShelf: "Da mesma prateleira.",
    format: "Formato", length: "Tamanho", language: "Idioma", updates: "Atualizações", refunds: "Reembolso", delivery: "Entrega",
    formatVal: "PDF · ePub · Notion", lengthVal: "≈ 90–140 páginas",
    langEnglish: "Inglês", langEspanol: "Espanhol", langPortugues: "Português",
    updatesVal: "Grátis por 12 meses", refundsVal: "7 dias, sem perguntas", deliveryVal: "Download imediato",
    notesHeroTitle1: "Ensaios curtos do", notesHeroTitle2: "escritório.",
    notesHeroLede: "Um diário mensal sobre o trabalho — o que estou publicando, o que estou cortando e o que aprendi lançando livros um de cada vez.",
    readTheNote: "Ler a nota →", readSuffix: "leitura",
    byAuthor: "Por Alvaro Abreu", previous: "← Anterior", next: "Próximo →",
    reviewsHeroTitle1: "Ferramentas e livros que eu", reviewsHeroTitle2: "pago e uso.",
    reviewsHeroLede: "Análises reais do software, serviços e livros que têm lugar no meu catálogo. Sem opiniões superficiais — só o que uso para trabalho real.",
    affDisclosure: "Aviso de afiliado.", affDisclosureText: "Alguns links me geram uma pequena comissão sem custo extra para você. Não analiso o que não uso e nunca aceito anúncios pagos.",
    affDisclosureShort: "Alguns links me geram uma pequena comissão sem custo extra para você. Só analiso o que uso.",
    readTheReview: "Ler a análise →", theVerdict: "O veredito",
    toolsHeroTitle1: "Ferramentas proprietárias para", toolsHeroTitle2: "problemas específicos.",
    toolsHeroLede: "Utilitários pequenos e focados criados pelo estúdio — calculadoras, frameworks de decisão e micro-apps projetados para resolver um problema específico para um profissional específico.",
    workshopOpen: "A oficina está aberta.",
    toolsBody1: "As primeiras ferramentas estão em desenvolvimento. Pense em: calculadoras de dedução fiscal para jurisdições específicas, simuladores de preços para editores independentes, checklists de conformidade que realmente verificam coisas.",
    toolsBody2: "Cada ferramenta é construída da mesma forma que os livros — para um leitor específico, resolvendo um problema específico, com preço justo. Algumas serão gratuitas. Algumas custarão o preço de um café. Nenhuma exigirá assinatura mensal.",
    whatToExpect: "O que esperar",
    calcTitle: "Calculadoras e simuladores", calcDesc: "Estimadores fiscais, modelos de preços, calculadoras de ROI — ferramentas que transformam suposições em números.",
    checkTitle: "Checklists interativas", checkDesc: "Checklists de conformidade, roteiros de lançamento e frameworks de auditoria que acompanham seu progresso.",
    workflowTitle: "Geradores de workflow", workflowDesc: "Geradores passo a passo para tarefas profissionais repetitivas — de contratos a planejamento de conteúdo.",
    decisionTitle: "Frameworks de decisão", decisionDesc: "Ferramentas estruturadas para decisões complexas — comparações de plataforma, contratar vs. automatizar, análise de mercado.",
    notifyLaunch: "Seja avisado quando ferramentas forem lançadas", firstTools: "Primeiras ferramentas no Q3 2026.",
    notifyBody: "Assine a nota mensal e seja o primeiro a saber quando cada ferramenta estiver no ar. Sem spam — apenas um email no primeiro domingo do mês.",
    notifyMe: "Me avise →",
    apName: "AnesthesiaPro", apTagline: "Precisão na ponta dos dedos.",
    apBlurb: "Calculadora de suporte à decisão clínica para anestesiologistas e residentes. Gestão de fluidos, dosagem de drogas, ventilação, ajustes pediátricos, bloqueios regionais — tudo a partir de um único perfil de paciente.",
    apFeat1: "Fluidos e perda sanguínea", apFeat1d: "Manutenção 4-2-1, déficit de jejum, volume sanguíneo estimado e perda admissível com recomendações em tempo real.",
    apFeat2: "Dosagem de drogas", apFeat2d: "Cálculos baseados em peso para agentes de indução, relaxantes musculares, vasopressores e drogas de emergência.",
    apFeat3: "Ventilação e pediatria", apFeat3d: "Volume corrente, frequência respiratória, parâmetros de ventilador, dimensionamento pediátrico de equipamentos e doses por idade.",
    apFeat4: "Bloqueios e técnicas", apFeat4d: "Referência de anestesia regional, calculadoras de infusão e registro de prática pessoal — tudo numa interface.",
    apBadge: "Grátis · App Web", apCta: "Abrir AnesthesiaPro →", apLang: "Disponível em PT · EN · ES", apTimer: "Timer cirúrgico integrado com rastreamento de fases",
    notifyLaunch2: "Fique por dentro", nextTwoShip: "Novas ferramentas saem sem aviso.",
    contactHeroTitle1: "Entre em contato, e", contactHeroTitle2: "eu respondo.",
    contactHeroLede: "O estúdio é uma pessoa. Leio todas as mensagens e respondo em até dois dias úteis — geralmente mais rápido.",
    sendMessage: "Envie uma mensagem", tellMe: "Me conte no que está trabalhando.",
    yourName: "Seu nome", email: "Email", topicLabel: "Assunto", message: "Mensagem",
    topicQ: "Uma pergunta sobre um livro", topicWrite: "Um tema que eu deveria escrever", topicPartner: "Ferramenta ou parceria", topicPress: "Imprensa ou entrevista", topicOther: "Outro assunto",
    sendBtn: "Enviar mensagem →", formFine: "Ao enviar, você concorda com uma resposta única. Seu email não é adicionado a nenhuma lista.",
    direct: "Direto", replyWithin: "Resposta em até", timezone: "Fuso horário", elsewhere: "Outros canais",
    wontReply: "O que eu não respondo", coldPitch: "Propostas comerciais frias", seoLink: "Trocas de links SEO", quickCall: "\"Calls rápidas\" sem pauta",
    aboutHeroTitle1: "Sobre o estúdio", aboutHeroTitle2: "Alvaro Abreu",
    aboutHeroLede: "Escrevo livros curtos e práticos para pessoas que preferem adotar bem uma ferramenta do que perseguir a próxima. O catálogo é pequeno de propósito, roda em três idiomas e cresce um ou dois títulos por semana.",
    whyExist: "Por que estes livros existem", howBuilt: "Como o catálogo é construído", whatWontDo: "O que eu não faço",
    studioNumbers: "O estúdio, em números", person: "pessoa",
    based: "Base", writingSince: "Escrevendo desde", distribution: "Distribuição", schedule: "Ritmo",
    closerTitle1: "Se algo disso faz sentido,", closerTitle2: "comece com um livro.",
    youre: "Você é",
    all: "Todos",
    successKicker: "PAGAMENTO CONFIRMADO",
    successTitle: "Pronto. O livro é seu.",
    successSub: "Verifique seu email — o link de download e o recibo estão a caminho. Se não aparecer em alguns minutos, confira a pasta de spam.",
    successDelivery: "Sua compra inclui download imediato em PDF + ePub, atualizações grátis por 12 meses e garantia de reembolso de 7 dias sem perguntas.",
    successBrowse: "Ver o catálogo →",
    successRelated: "Você também pode gostar destes.",
  },
  es: {
    catalog: "Catálogo", notes: "Notas", reviews: "Reseñas", tools: "Herramientas", about: "Acerca de", contact: "Contacto",
    tagline: "Independiente · Estudio",
    footerTag: "Libros cortos, tres idiomas, un catálogo.",
    statusPill: "Nuevo título cada semana · Actualizado mayo 2026",
    heroTitle1: "Libros cortos para el", heroTitle2: "operador silencioso.",
    heroSub: "Un catálogo creciente de manuales prácticos sobre IA, trabajo y los pequeños sistemas que se acumulan. Escrito en inglés, español y portugués — publicado de forma independiente, con precio justo.",
    browseCatalog: "Ver el catálogo →", aboutWork: "Sobre el trabajo",
    titlesInPrint: "Títulos publicados", languages: "Idiomas", markets: "Mercados", priceFloor: "Precio mínimo", lastUpdate: "Última actualización",
    featured: "Destacados", featuredTitle: "Tres lecturas, elegidas este mes.", edition: "EDICIÓN 2026",
    readMore: "Leer más →", viewArrow: "Ver →",
    theCatalog: "El catálogo", everythingSorted: "Todo, ordenado.",
    titles: "títulos", titlesIn: "títulos en",
    topic: "Tema", market: "Mercado", allMarkets: "Todos los mercados",
    buyArrow: "Comprar →", oneTime: "Pago único. Acceso de por vida.", yoursToday: "Tuyo hoy.",
    instantDelivery: "Entrega inmediata. Reembolso en 7 días. Actualizaciones gratis por 12 meses.",
    whatsInside: "Qué hay dentro", shortChapters: "capítulos cortos.", oneClrPath: "Un camino claro.",
    forYouIf: "Esto es para ti si…", notForYou: "Probablemente no es para ti si…",
    forYou1: "y quieres un manual práctico, no una teoría.",
    forYou2: "Prefieres leer una vez y aplicar que hacer scroll infinito.",
    forYou3: "Crees que los pequeños pasos compuestos le ganan al hype.",
    notForYou1: "Quieres un tratado exhaustivo de 500 páginas.",
    notForYou2: "Buscas citas académicas en vez de pasos prácticos.",
    notForYou3: "Esperas coaching personal incluido.",
    readNext: "Lee a continuación", fromSameShelf: "Del mismo estante.",
    format: "Formato", length: "Extensión", language: "Idioma", updates: "Actualizaciones", refunds: "Reembolso", delivery: "Entrega",
    formatVal: "PDF · ePub · Notion", lengthVal: "≈ 90–140 páginas",
    langEnglish: "Inglés", langEspanol: "Español", langPortugues: "Portugués",
    updatesVal: "Gratis por 12 meses", refundsVal: "7 días, sin preguntas", deliveryVal: "Descarga inmediata",
    notesHeroTitle1: "Ensayos cortos desde el", notesHeroTitle2: "escritorio.",
    notesHeroLede: "Un diario mensual sobre el trabajo — qué estoy publicando, qué estoy cortando y qué aprendí lanzando libros uno a la vez.",
    readTheNote: "Leer la nota →", readSuffix: "lectura",
    byAuthor: "Por Alvaro Abreu", previous: "← Anterior", next: "Siguiente →",
    reviewsHeroTitle1: "Herramientas y libros que", reviewsHeroTitle2: "pago y uso.",
    reviewsHeroLede: "Reseñas reales del software, servicios y libros que tienen lugar en mi catálogo. Sin opiniones superficiales — solo lo que uso para trabajo real.",
    affDisclosure: "Aviso de afiliado.", affDisclosureText: "Algunos enlaces me generan una pequeña comisión sin costo extra para ti. No reseño lo que no uso y nunca acepto anuncios pagados.",
    affDisclosureShort: "Algunos enlaces me generan una pequeña comisión sin costo extra para ti. Solo reseño lo que uso.",
    readTheReview: "Leer la reseña →", theVerdict: "El veredicto",
    toolsHeroTitle1: "Herramientas propias para", toolsHeroTitle2: "problemas específicos.",
    toolsHeroLede: "Utilidades pequeñas y enfocadas construidas por el estudio — calculadoras, frameworks de decisión y micro-apps diseñadas para resolver un problema específico.",
    workshopOpen: "El taller está abierto.",
    toolsBody1: "Las primeras herramientas están en desarrollo. Piensa en: calculadoras de deducciones fiscales para jurisdicciones específicas, simuladores de precios para editores independientes, checklists de cumplimiento que realmente verifican cosas.",
    toolsBody2: "Cada herramienta se construye igual que los libros — para un lector específico, resolviendo un problema específico, con precio justo. Algunas serán gratuitas. Algunas costarán el precio de un café. Ninguna requerirá suscripción mensual.",
    whatToExpect: "Qué esperar",
    calcTitle: "Calculadoras y simuladores", calcDesc: "Estimadores fiscales, modelos de precios, calculadoras de ROI — herramientas que convierten suposiciones en números.",
    checkTitle: "Checklists interactivas", checkDesc: "Checklists de cumplimiento, guías de lanzamiento y frameworks de auditoría que siguen tu progreso.",
    workflowTitle: "Generadores de workflow", workflowDesc: "Generadores paso a paso para tareas profesionales repetitivas — desde contratos hasta planificación de contenido.",
    decisionTitle: "Frameworks de decisión", decisionDesc: "Herramientas estructuradas para decisiones complejas — comparaciones de plataforma, contratar vs. automatizar, análisis de mercado.",
    notifyLaunch: "Recibe aviso cuando lancemos herramientas", firstTools: "Primeras herramientas en Q3 2026.",
    notifyBody: "Suscríbete a la nota mensual y sé el primero en saber cuando cada herramienta esté disponible. Sin spam — solo un email el primer domingo del mes.",
    notifyMe: "Avísame →",
    apName: "AnesthesiaPro", apTagline: "Precisión en la punta de los dedos.",
    apBlurb: "Calculadora de soporte a la decisión clínica para anestesiólogos y residentes. Gestión de fluidos, dosificación de fármacos, ventilación, ajustes pediátricos, bloqueos regionales — todo desde un único perfil de paciente.",
    apFeat1: "Fluidos y pérdida sanguínea", apFeat1d: "Mantenimiento 4-2-1, déficit de ayuno, volumen sanguíneo estimado y pérdida admisible con recomendaciones en tiempo real.",
    apFeat2: "Dosificación de fármacos", apFeat2d: "Cálculos basados en peso para agentes de inducción, relajantes musculares, vasopresores y fármacos de emergencia.",
    apFeat3: "Ventilación y pediatría", apFeat3d: "Volumen tidal, frecuencia respiratoria, parámetros del ventilador, dimensionamiento pediátrico de equipos y dosis por edad.",
    apFeat4: "Bloqueos y técnicas", apFeat4d: "Referencia de anestesia regional, calculadoras de infusión y registro de práctica personal — todo en una interfaz.",
    apBadge: "Gratis · App Web", apCta: "Abrir AnesthesiaPro →", apLang: "Disponible en PT · EN · ES", apTimer: "Timer quirúrgico integrado con seguimiento de fases",
    notifyLaunch2: "Mantente al tanto", nextTwoShip: "Nuevas herramientas salen sin aviso.",
    contactHeroTitle1: "Escríbeme, y", contactHeroTitle2: "te respondo.",
    contactHeroLede: "El estudio es una persona. Leo cada mensaje y respondo en máximo dos días hábiles — generalmente más rápido.",
    sendMessage: "Envía un mensaje", tellMe: "Cuéntame en qué estás trabajando.",
    yourName: "Tu nombre", email: "Email", topicLabel: "Tema", message: "Mensaje",
    topicQ: "Una pregunta sobre un libro", topicWrite: "Un tema que debería escribir", topicPartner: "Herramienta o alianza", topicPress: "Prensa o entrevista", topicOther: "Otro tema",
    sendBtn: "Enviar mensaje →", formFine: "Al enviar, aceptas una respuesta única. Tu email no se agrega a ninguna lista.",
    direct: "Directo", replyWithin: "Respuesta en", timezone: "Zona horaria", elsewhere: "Otros canales",
    wontReply: "Lo que no respondo", coldPitch: "Propuestas comerciales frías", seoLink: "Intercambios de links SEO", quickCall: "\"Llamadas rápidas\" sin agenda",
    aboutHeroTitle1: "Sobre el estudio", aboutHeroTitle2: "Alvaro Abreu",
    aboutHeroLede: "Escribo libros cortos y prácticos para personas que prefieren adoptar bien una herramienta que perseguir la siguiente. El catálogo es pequeño a propósito, corre en tres idiomas y crece uno o dos títulos por semana.",
    whyExist: "Por qué existen estos libros", howBuilt: "Cómo se construye el catálogo", whatWontDo: "Lo que no haré",
    studioNumbers: "El estudio, en números", person: "persona",
    based: "Base", writingSince: "Escribiendo desde", distribution: "Distribución", schedule: "Ritmo",
    closerTitle1: "Si algo de esto resuena,", closerTitle2: "empieza con un libro.",
    youre: "Eres",
    all: "Todos",
    successKicker: "PAGO CONFIRMADO",
    successTitle: "Listo. El libro es tuyo.",
    successSub: "Revisa tu email — el enlace de descarga y el recibo están en camino. Si no lo ves en unos minutos, revisa tu carpeta de spam.",
    successDelivery: "Tu compra incluye descarga inmediata en PDF + ePub, actualizaciones gratis por 12 meses y garantía de reembolso de 7 días sin preguntas.",
    successBrowse: "Ver el catálogo →",
    successRelated: "También te pueden gustar estos.",
  },
};

const useI18n = (lang) => (key) => I18N[lang]?.[key] || I18N.en[key] || key;

const useBreakpoint = () => {
  const getBreakpoint = () => {
    if (typeof window === "undefined") return { isMobile: false, isTablet: false, isDesktop: true };
    const w = window.innerWidth;
    return { isMobile: w < 768, isTablet: w >= 768 && w <= 1024, isDesktop: w > 1024 };
  };
  const [bp, setBp] = React.useState(getBreakpoint);
  React.useEffect(() => {
    const handler = () => setBp(getBreakpoint());
    window.addEventListener("resize", handler);
    return () => window.removeEventListener("resize", handler);
  }, []);
  return bp;
};

const THEME_LIGHT = {
  stuPaper: "#f6f4ee", stuInk: "#1a1f1c", stuCard: "#ffffff", stuMute: "#6c6f68",
  stuRule: "#e2dfd5", stuAccent: "#5d6e4c", stuAccentSoft: "#dde2cf",
  heroSubColor: "#3a403b", splitItemColor: "#2a2f2c", ctaSubColor: "#bdc4b8",
  subKickerColor: "#9aa68d", subBodyColor: "#c5c8be", subFineColor: "#7d8278",
  blurbColor: "#5a5e58", articleLedeColor: "#2a2f2c",
  disclosureColor: "#3d4a31", disclosureBg: "#dde2cf",
  coverBg: "#e9e6db",
  coPaper: "#f9f7f1", coPaper2: "#f0ede4", coInk: "#2a2318", coInk2: "#52493a", coInk3: "#7a7060",
  coRule: "#d8d3c6", coRuleS: "#b5af9f", coJade: "#3a7a5c", coJadeBg: "#e6f3ec",
  coTerra: "#b05a30", coTerraBg: "#f5e8df", coGold: "#c09a40",
};
const THEME_DARK = {
  stuPaper: "#151a16", stuInk: "#e8e4dc", stuCard: "#1e2420", stuMute: "#9a9d96",
  stuRule: "#3a3f3a", stuAccent: "#8ba67a", stuAccentSoft: "#2e3a28",
  heroSubColor: "#b0b5ac", splitItemColor: "#ccc9c2", ctaSubColor: "#8a9184",
  subKickerColor: "#7a8a6d", subBodyColor: "#8a8d84", subFineColor: "#6a6e64",
  blurbColor: "#a0a49c", articleLedeColor: "#ccc9c2",
  disclosureColor: "#a0b890", disclosureBg: "#2e3a28",
  coverBg: "#252a26",
  coPaper: "#1a1e1a", coPaper2: "#222822", coInk: "#e0dcd4", coInk2: "#b0a898", coInk3: "#8a8474",
  coRule: "#3a3f3a", coRuleS: "#4a504a", coJade: "#6aaa8c", coJadeBg: "#1e3028",
  coTerra: "#d08050", coTerraBg: "#2e2018", coGold: "#d0aa50",
};

const _ckGet = (n) => { var m = document.cookie.match(new RegExp('(?:^| )' + n + '=([^;]+)')); return m ? m[1] : null; };
const _ckSet = (n, v) => { document.cookie = n + '=' + v + ';domain=.alvaroabreu.com;path=/;max-age=31536000;SameSite=Lax'; };

const useTheme = () => {
  const [dark, setDark] = React.useState(() => {
    if (typeof window === "undefined") return false;
    const stored = _ckGet("stu-theme") || localStorage.getItem("stu-theme");
    if (stored) return stored === "dark";
    return window.matchMedia?.("(prefers-color-scheme: dark)").matches || false;
  });

  React.useEffect(() => {
    const t = dark ? THEME_DARK : THEME_LIGHT;
    const s = document.documentElement.style;
    Object.entries(t).forEach(([k, v]) => { s.setProperty("--" + k, v); });
    var val = dark ? "dark" : "light";
    localStorage.setItem("stu-theme", val);
    _ckSet("stu-theme", val);
  }, [dark]);

  return { dark, toggle: () => setDark(d => !d) };
};

/* ─── Mobile menu overlay ─────────────────────────────────── */

const MobileMenu = ({ open, onClose, view, t, onHome, onAbout, onNotes, onReviews, onContact, onTools, lang, setLang, dark, toggleTheme }) => {
  if (!open) return null;
  const linkStyle = { display: "block", padding: "16px 0", fontSize: 22, fontFamily: '"Fraunces", Georgia, serif', color: "var(--stuInk)", borderBottom: "1px solid var(--stuRule)", cursor: "pointer", textDecoration: "none" };
  const activeMark = { color: "var(--stuAccent)" };
  const wrap = (fn) => () => { fn(); onClose(); };
  return (
    <div style={{ position: "fixed", inset: 0, background: "var(--stuPaper)", zIndex: 100, display: "flex", flexDirection: "column", overflow: "auto" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "16px 16px" }}>
        <div style={{ fontFamily: '"Fraunces", Georgia, serif', fontSize: 18, color: "var(--stuInk)" }}>Menu</div>
        <button onClick={onClose} style={{ background: "none", border: "none", fontSize: 28, cursor: "pointer", color: "var(--stuInk)", padding: 8 }} aria-label="Close menu">&times;</button>
      </div>
      <nav style={{ padding: "0 24px", flex: 1 }}>
        <a style={{...linkStyle, ...(view === "home" || view === "product" ? activeMark : {})}} onClick={wrap(onHome)}>{t("catalog")}</a>
        <a style={{...linkStyle, ...(view === "notes" || view === "noteDetail" ? activeMark : {})}} onClick={wrap(onNotes)}>{t("notes")}</a>
        <a style={{...linkStyle}} href="https://reviews.alvaroabreu.com/">{t("reviews")}</a>
        <a style={{...linkStyle, ...(view === "tools" ? activeMark : {})}} onClick={wrap(onTools)}>{t("tools")}</a>
        <a style={{...linkStyle, ...(view === "contact" ? activeMark : {})}} onClick={wrap(onContact)}>{t("contact")}</a>
      </nav>
      <div style={{ padding: "24px", display: "flex", justifyContent: "space-between", alignItems: "center", borderTop: "1px solid var(--stuRule)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", border: "1px solid var(--stuRule)", borderRadius: 999, fontSize: 12 }}>
          {["en", "pt", "es"].map((l, i) => (
            <React.Fragment key={l}>
              {i > 0 && <span style={{ color: "var(--stuRule)" }}>·</span>}
              <span style={lang === l ? { color: "var(--stuInk)", fontWeight: 600 } : { color: "var(--stuMute)", cursor: "pointer" }} onClick={() => setLang(l)}>{l.toUpperCase()}</span>
            </React.Fragment>
          ))}
        </div>
        <button onClick={toggleTheme} style={{ background: "none", border: "1px solid var(--stuRule)", borderRadius: 999, padding: "6px 12px", fontSize: 18, cursor: "pointer", color: "var(--stuInk)" }} aria-label="Toggle theme">{dark ? "☀" : "☾"}</button>
      </div>
    </div>
  );
};

const StudioV2 = () => {
  const [view, setView] = React.useState("home");
  const [selectedId, setSelectedId] = React.useState(null);
  const [selectedNoteId, setSelectedNoteId] = React.useState(null);
  const [selectedReviewId, setSelectedReviewId] = React.useState(null);
  const [lang, _setLang] = React.useState(() => { var c = _ckGet("stu-lang"); return c && /^(en|pt|es)$/.test(c) ? c : "en"; });
  const setLang = (l) => { _setLang(l); _ckSet("stu-lang", l); };
  const [menuOpen, setMenuOpen] = React.useState(false);
  const t = useI18n(lang);
  const { isMobile, isTablet, isDesktop } = useBreakpoint();
  const { dark, toggle: toggleTheme } = useTheme();
  const products = window.PRODUCTS;

  /* Handle ?payment=success/cancelled from Stripe redirect */
  React.useEffect(() => {
    var params = new URLSearchParams(window.location.search);
    if (params.get("payment") === "success") {
      var pidRaw = params.get("product") || "0";
      var pid = isNaN(Number(pidRaw)) ? pidRaw : Number(pidRaw);
      window.history.replaceState(null, "", "/success/" + pid);
      setSelectedId(pid); setView("success");
    } else if (params.get("payment") === "cancelled") {
      var pid2Raw = params.get("product") || "0";
      var pid2 = isNaN(Number(pid2Raw)) ? pid2Raw : Number(pid2Raw);
      window.history.replaceState(null, "", "/product/" + pid2);
      setSelectedId(pid2); setView("product");
    }
  }, []);

  /* Path routing — clean URLs for SEO (with hash backward compat) */
  React.useEffect(() => {
    var parseRoute = function() {
      var hash = (window.location.hash || "").replace(/^#\/?/, "");
      var path = window.location.pathname;
      if (hash && (path === "/" || path === "")) {
        window.history.replaceState(null, "", "/" + hash);
        path = "/" + hash;
      }
      var parts = path.replace(/^\//, "").split("/").filter(Boolean);
      var parseId = function(v) { var n = Number(v); return isNaN(n) ? v : n; };
      if (parts[0] === "product" && parts[1]) { setSelectedId(parseId(parts[1])); setView("product"); }
      else if (parts[0] === "checkout" && parts[1]) { setSelectedId(parseId(parts[1])); setView("checkout"); }
      else if (parts[0] === "note" && parts[1]) { setSelectedNoteId(parts[1]); setView("noteDetail"); }
      else if (parts[0] === "review" && parts[1]) { setSelectedReviewId(parts[1]); setView("reviewDetail"); }
      else if (parts[0] === "notes") { setView("notes"); }
      else if (parts[0] === "reviews") { setView("reviews"); }
      else if (parts[0] === "about") { setView("about"); }
      else if (parts[0] === "contact") { setView("contact"); }
      else if (parts[0] === "tools") { setView("tools"); }
      else if (parts[0] === "privacy") { setView("privacy"); }
      else if (parts[0] === "terms") { setView("terms"); }
      else if (parts[0] === "refunds") { setView("refunds"); }
      else if (parts[0] === "success" && parts[1]) { setSelectedId(parseId(parts[1])); setView("success"); }
      else { setView("home"); }
      window.scrollTo?.(0, 0);
    };
    parseRoute();
    var onPop = function() { parseRoute(); };
    var onHashCompat = function() {
      var h = (window.location.hash || "").replace(/^#\/?/, "");
      if (h) { window.history.replaceState(null, "", "/" + h); parseRoute(); }
    };
    window.addEventListener("popstate", onPop);
    window.addEventListener("hashchange", onHashCompat);
    return function() { window.removeEventListener("popstate", onPop); window.removeEventListener("hashchange", onHashCompat); };
  }, []);

  var navTo = function(path) { window.history.pushState(null, "", path); window.dispatchEvent(new PopStateEvent("popstate")); };
  const goProduct  = (id) => { navTo("/product/" + id); };
  const goCheckout = (id) => { navTo("/checkout/" + id); };
  const goHome     = () => { navTo("/"); };
  const goAbout    = () => { navTo("/about"); };
  const goNotes    = () => { navTo("/notes"); };
  const goNote     = (id) => { navTo("/note/" + id); };
  const goReviews  = () => { navTo("/reviews"); };
  const goReview   = (id) => { navTo("/review/" + id); };
  const goContact  = () => { navTo("/contact"); };
  const goTools    = () => { navTo("/tools"); };
  const goPrivacy  = () => { navTo("/privacy"); };
  const goTerms    = () => { navTo("/terms"); };
  const goRefunds  = () => { navTo("/refunds"); };

  const navProps = { view, lang, t, setLang, isMobile, isTablet, isDesktop, onHome: goHome, onAbout: goAbout, onNotes: goNotes, onReviews: goReviews, onContact: goContact, onTools: goTools, dark, toggleTheme, menuOpen, setMenuOpen };
  const selected = products.find(p => p.id === selectedId);
  const localNotes = getLocalizedNotes(lang);
  const localReviews = getLocalizedReviews(lang);
  const selectedNote = localNotes.find(n => n.id === selectedNoteId);
  const selectedReview = localReviews.find(r => r.id === selectedReviewId);

  /* Close mobile menu on navigation */
  React.useEffect(() => { setMenuOpen(false); }, [view]);

  /* Update <html lang> when language changes */
  React.useEffect(() => { document.documentElement.lang = lang; }, [lang]);

  /* Dynamic document.title per route */
  React.useEffect(() => {
    var base = "Alvaro Abreu — Independent Studio";
    var titles = {
      home: base,
      product: selected ? selected.title + " — Alvaro Abreu" : base,
      checkout: selected ? "Checkout · " + selected.title : base,
      notes: "Notes — Alvaro Abreu",
      noteDetail: selectedNote ? selectedNote.title + " — Notes" : "Notes — Alvaro Abreu",
      reviews: "Reviews — Alvaro Abreu",
      reviewDetail: selectedReview ? selectedReview.title + " — Reviews" : "Reviews — Alvaro Abreu",
      about: "About — Alvaro Abreu",
      contact: "Contact — Alvaro Abreu",
      tools: "Tools — Alvaro Abreu",
      privacy: "Privacy Policy — Alvaro Abreu",
      terms: "Terms of Service — Alvaro Abreu",
      refunds: "Refund Policy — Alvaro Abreu",
      success: selected ? "Thank you! — " + selected.title : base
    };
    document.title = titles[view] || base;
  }, [view, selectedId, selectedNoteId, selectedReviewId]);

  /* Checkout has its own header/footer — skip shared chrome */
  if (view === "checkout" && selected) {
    return (
      <div style={stuStyles.page}>
        <StudioCheckout product={selected} products={products} onBack={() => goProduct(selected.id)} onProduct={goProduct} t={t} lang={lang} isMobile={isMobile} isTablet={isTablet} />
      </div>
    );
  }

  return (
    <div style={stuStyles.page}>
      <StudioHeader {...navProps} />
      <MobileMenu open={menuOpen} onClose={() => setMenuOpen(false)} view={view} t={t} onHome={goHome} onAbout={goAbout} onNotes={goNotes} onReviews={goReviews} onContact={goContact} onTools={goTools} lang={lang} setLang={setLang} dark={dark} toggleTheme={toggleTheme} />
      {view === "home"         && <StudioHome products={products} onProduct={goProduct} onAbout={goAbout} t={t} lang={lang} isMobile={isMobile} isTablet={isTablet} />}
      {view === "product"      && selected && <StudioProduct product={selected} products={products} onBack={goHome} onProduct={goProduct} onCheckout={goCheckout} t={t} lang={lang} isMobile={isMobile} isTablet={isTablet} />}
      {view === "about"        && <StudioAbout onHome={goHome} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "notes"        && <StudioNotes notes={localNotes} onNote={goNote} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "noteDetail"   && selectedNote && <StudioNoteDetail note={selectedNote} notes={localNotes} onBack={goNotes} onNote={goNote} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "reviews"      && <StudioReviews reviews={localReviews} onReview={goReview} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "reviewDetail" && selectedReview && <StudioReviewDetail review={selectedReview} reviews={localReviews} onBack={goReviews} onReview={goReview} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "contact"      && <StudioContact t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "tools"        && <StudioTools t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "privacy"      && <StudioLegal page="privacy" onHome={goHome} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "terms"        && <StudioLegal page="terms" onHome={goHome} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "refunds"      && <StudioLegal page="refunds" onHome={goHome} t={t} isMobile={isMobile} isTablet={isTablet} />}
      {view === "success"      && selected && <StudioSuccess product={selected} products={products} onHome={goHome} onProduct={goProduct} t={t} isMobile={isMobile} isTablet={isTablet} />}
      <StudioFooter {...navProps} t={t} />
    </div>
  );
};

/* ─── Shared chrome ─────────────────────────────────────────── */

const StudioHeader = ({ view, lang, t, setLang, isMobile, isTablet, onHome, onAbout, onNotes, onReviews, onContact, onTools, dark, toggleTheme, menuOpen, setMenuOpen }) => (
  <header style={stuStyles.header}>
    <div style={{...stuStyles.headerInner, ...(isMobile ? { gridTemplateColumns: "1fr auto auto", padding: "14px 16px", gap: 10 } : isTablet ? { padding: "16px 32px" } : {})}}>
      <div style={stuStyles.brand} onClick={onHome}>
        <div style={stuStyles.brandBlock}>A</div>
        <div style={stuStyles.brandStack}>
          <div style={stuStyles.brandStackTop}>Alvaro Abreu</div>
          <div style={stuStyles.brandStackBot}>{t("tagline")}</div>
        </div>
      </div>
      {!isMobile && (
        <nav style={{...stuStyles.nav, ...(isTablet ? { gap: 16 } : {})}}>
          <a style={{...stuStyles.navLink, ...(view === "home"    || view === "product" ? stuStyles.navLinkActive : {})}} onClick={onHome}>{t("catalog")}</a>
          <a style={{...stuStyles.navLink, ...(view === "notes"   || view === "noteDetail" ? stuStyles.navLinkActive : {})}} onClick={onNotes}>{t("notes")}</a>
          <a style={{...stuStyles.navLink}} href="https://reviews.alvaroabreu.com/">{t("reviews")}</a>
          <a style={{...stuStyles.navLink, ...(view === "tools"   ? stuStyles.navLinkActive : {})}} onClick={onTools}>{t("tools")}</a>
          <a style={{...stuStyles.navLink, ...(view === "contact" ? stuStyles.navLinkActive : {})}} onClick={onContact}>{t("contact")}</a>
        </nav>
      )}
      <div style={stuStyles.headerRight}>
        <button onClick={toggleTheme} style={{ background: "none", border: "1px solid var(--stuRule)", borderRadius: 999, width: 34, height: 34, fontSize: 16, cursor: "pointer", color: "var(--stuInk)", display: "grid", placeItems: "center", padding: 0 }} aria-label="Toggle theme">{dark ? "☀" : "☾"}</button>
        {!isMobile && (
          <div style={stuStyles.langSwitch}>
            {["en", "pt", "es"].map((l, i) => (
              <React.Fragment key={l}>
                {i > 0 && <span style={stuStyles.langDot}>·</span>}
                <span style={lang === l ? stuStyles.langActive : stuStyles.lang} onClick={() => setLang(l)}>{l.toUpperCase()}</span>
              </React.Fragment>
            ))}
          </div>
        )}
        {isMobile && (
          <button onClick={() => setMenuOpen(!menuOpen)} style={{ background: "none", border: "1px solid var(--stuRule)", borderRadius: 8, width: 38, height: 38, fontSize: 22, cursor: "pointer", color: "var(--stuInk)", display: "grid", placeItems: "center", padding: 0 }} aria-label="Menu">{menuOpen ? "×" : "☰"}</button>
        )}
      </div>
    </div>
  </header>
);

const StudioFooter = ({ t, isMobile, isTablet, onHome, onAbout, onNotes, onReviews, onContact, onTools }) => (
  <footer style={{...stuStyles.footer, ...(isMobile ? { padding: "32px 16px 24px" } : isTablet ? { padding: "40px 32px 28px" } : {})}}>
    {isMobile && (
      <div style={{ display: "flex", flexWrap: "wrap", gap: "8px 16px", marginBottom: 24, paddingBottom: 20, borderBottom: "1px solid var(--stuRule)" }}>
        <a style={stuStyles.footerLink} onClick={onHome}>{t("catalog")}</a>
        <a style={stuStyles.footerLink} onClick={onNotes}>{t("notes")}</a>
        <a style={stuStyles.footerLink} href="https://reviews.alvaroabreu.com/">{t("reviews")}</a>
        <a style={stuStyles.footerLink} onClick={onTools}>{t("tools")}</a>
        <a style={stuStyles.footerLink} onClick={onContact}>{t("contact")}</a>
      </div>
    )}
    <div style={{...stuStyles.footerInner, ...(isMobile ? { flexDirection: "column", gap: 20 } : {})}}>
      <div style={stuStyles.footerL}>
        <div style={stuStyles.footerBrandLockup}>
          <div style={{...stuStyles.brandBlock, width: 56, height: 56, fontSize: 40}}>A</div>
          <div style={stuStyles.brandStack}>
            <div style={{...stuStyles.brandStackTop, fontSize: 26}}>Alvaro Abreu</div>
            <div style={{...stuStyles.brandStackBot, fontSize: 10, marginTop: 6}}>{t("tagline")}</div>
          </div>
        </div>
        <div style={{...stuStyles.footerTag, marginTop: 14}}>{t("footerTag")}</div>
      </div>
      {!isMobile && (
        <div style={stuStyles.footerR}>
          <a style={stuStyles.footerLink} onClick={onHome}>{t("catalog")}</a>
          <a style={stuStyles.footerLink} onClick={onNotes}>{t("notes")}</a>
          <a style={stuStyles.footerLink} href="https://reviews.alvaroabreu.com/">{t("reviews")}</a>
          <a style={stuStyles.footerLink} onClick={onTools}>{t("tools")}</a>
          <a style={stuStyles.footerLink} onClick={onContact}>{t("contact")}</a>
        </div>
      )}
    </div>
    <div style={{...stuStyles.footerBot, ...(isMobile ? { flexDirection: "column", gap: 8, textAlign: "center" } : {})}}>
      <span>© 2026 Alvaro Abreu · São Paulo</span>
      <div style={{ display: "flex", gap: 16, fontSize: 12 }}>
        <a style={{ ...stuStyles.footerLink, fontSize: 12 }} href="/privacy" onClick={(e) => { e.preventDefault(); goPrivacy(); }}>Privacy</a>
        <a style={{ ...stuStyles.footerLink, fontSize: 12 }} href="/terms" onClick={(e) => { e.preventDefault(); goTerms(); }}>Terms</a>
        <a style={{ ...stuStyles.footerLink, fontSize: 12 }} href="/refunds" onClick={(e) => { e.preventDefault(); goRefunds(); }}>Refunds</a>
      </div>
    </div>
  </footer>
);

/* ─── Home view ─────────────────────────────────────────────── */

const StudioHome = ({ products, onProduct, onAbout, t, lang, isMobile, isTablet }) => {
  const [topic, setTopic] = React.useState("All");
  const [market, setMarket] = React.useState("ALL");

  const marketProducts = market === "ALL" ? products : products.filter(p => p.market === market);
  const topicFilters = ["All", ...Array.from(new Set(marketProducts.map(p => p.category)))];

  const handleMarket = (code) => { setMarket(code); setTopic("All"); };

  const marketFilters = [
    { code: "ALL", label: t("allMarkets"), flag: "—" },
    { code: "US",  label: "United States", flag: "US" },
    { code: "UK",  label: "United Kingdom", flag: "UK" },
    { code: "ES",  label: "España", flag: "ES" },
    { code: "AU",  label: "Australia", flag: "AU" },
    { code: "BR",  label: "Brasil", flag: "BR" },
  ];

  const filtered = products.filter(p =>
    (topic === "All" || p.category === topic) &&
    (market === "ALL" || p.market === market)
  );
  const activeMarketLabel = marketFilters.find(m => m.code === market)?.label;

  const featuredHero = products[0] || null;
  const featuredA    = products[5] || null;
  const featuredB    = products[10] || null;
  const hasFeatured  = featuredHero && featuredA && featuredB;

  return (
    <React.Fragment>
      {/* Hero — refined: editorial split, one big number, working subhead */}
      <section style={{...stuStyles.hero, ...(isMobile ? { gridTemplateColumns: "1fr", gap: 32, padding: "48px 16px" } : isTablet ? { gridTemplateColumns: "1fr", gap: 40, padding: "60px 32px" } : {})}}>
        <div style={stuStyles.heroLeft}>
          <div style={stuStyles.statusPill}>
            <span style={stuStyles.statusDot}></span>
            {t("statusPill")}
          </div>
          <h1 style={{...stuStyles.heroTitle, ...(isMobile ? { fontSize: 40, letterSpacing: -1 } : isTablet ? { fontSize: 52, letterSpacing: -1.4 } : {})}}>
            {t("heroTitle1")}<br/>
            <span style={stuStyles.heroAccent}>{t("heroTitle2")}</span>
          </h1>
          <p style={{...stuStyles.heroSub, ...(isMobile ? { fontSize: 16 } : {})}}>{t("heroSub")}</p>
          <div style={{...stuStyles.heroCtas, ...(isMobile ? { flexDirection: "column", alignItems: "stretch" } : {})}}>
            <a style={{...stuStyles.btnPrimary, ...(isMobile ? { textAlign: "center" } : {})}} onClick={() => document.getElementById("catalog")?.scrollIntoView?.({behavior: "smooth"})}>{t("browseCatalog")}</a>
            <a style={{...stuStyles.btnGhost, ...(isMobile ? { textAlign: "center" } : {})}} onClick={onAbout}>{t("aboutWork")}</a>
          </div>
        </div>

        <div style={stuStyles.heroRight}>
          <div style={{...stuStyles.statCard, ...((isMobile || isTablet) ? { maxWidth: "100%" } : {})}}>
            <div style={stuStyles.statRow}><span style={stuStyles.statLbl}>{t("titlesInPrint")}</span><span style={stuStyles.statVal}>{products.length}</span></div>
            <div style={stuStyles.statDiv}></div>
            <div style={stuStyles.statRow}><span style={stuStyles.statLbl}>{t("languages")}</span><span style={stuStyles.statVal}>EN · PT · ES</span></div>
            <div style={stuStyles.statDiv}></div>
            <div style={stuStyles.statRow}><span style={stuStyles.statLbl}>{t("markets")}</span><span style={stuStyles.statVal}>US · UK · ES · AU · BR</span></div>
            <div style={stuStyles.statDiv}></div>
            <div style={stuStyles.statRow}><span style={stuStyles.statLbl}>{t("priceFloor")}</span><span style={stuStyles.statVal}>$7</span></div>
            <div style={stuStyles.statDiv}></div>
            <div style={stuStyles.statRow}><span style={stuStyles.statLbl}>{t("lastUpdate")}</span><span style={stuStyles.statVal}>May 8, 2026</span></div>
          </div>
        </div>
      </section>

      {/* Featured spread — 1 large + 2 small (only if enough products) */}
      {hasFeatured && <section style={{...stuStyles.feature, ...(isMobile ? { padding: "40px 16px" } : isTablet ? { padding: "60px 32px" } : {})}}>
        <div style={stuStyles.featHead}>
          <div style={stuStyles.kicker}>{t("featured")}</div>
          <h2 style={{...stuStyles.featTitle, ...(isMobile ? { fontSize: 28 } : isTablet ? { fontSize: 36 } : {})}}>{t("featuredTitle")}</h2>
        </div>
        <div style={{...stuStyles.featSpread, ...(isMobile ? { gridTemplateColumns: "1fr", gap: 20 } : isTablet ? { gridTemplateColumns: "1fr", gap: 24 } : {})}}>
          {/* Hero card */}
          <article style={{...stuStyles.featHeroCard, ...((isMobile || isTablet) ? { gridTemplateColumns: "1fr" } : {})}} onClick={() => onProduct(featuredHero.id)}>
            <div style={stuStyles.featHeroCover}>
              <div style={stuStyles.featHeroFrame}>
                <div style={stuStyles.featHeroNum}>№ {String(featuredHero.id).padStart(2, "0")}</div>
                <div>
                  <div style={stuStyles.featHeroEyebrow}>{featuredHero.category}</div>
                  <div style={{...stuStyles.featHeroTitle, ...(isMobile ? { fontSize: 24 } : {})}}>{featuredHero.title}</div>
                </div>
                <div style={stuStyles.featHeroMeta}>
                  <span>{t("edition")}</span>
                  <span>{featuredHero.market}</span>
                </div>
              </div>
            </div>
            <div style={{...stuStyles.featHeroBody, ...(isMobile ? { padding: 20 } : {})}}>
              <div style={stuStyles.featCat}>{featuredHero.category} · For {featuredHero.audience}</div>
              <h3 style={{...stuStyles.featHeroH3, ...(isMobile ? { fontSize: 22 } : {})}}>{featuredHero.title}</h3>
              <p style={stuStyles.featHeroBlurb}>{featuredHero.blurb}</p>
              <div style={stuStyles.featFoot}>
                <span style={stuStyles.featPrice}>{featuredHero.price}</span>
                <span style={stuStyles.featLink}>{t("readMore")}</span>
              </div>
            </div>
          </article>

          {/* Small stack */}
          <div style={{...stuStyles.featSmallCol, ...(isTablet ? { flexDirection: "row" } : {})}}>
            {[featuredA, featuredB].map(p => (
              <article key={p.id} style={stuStyles.featSmallCard} onClick={() => onProduct(p.id)}>
                <div style={stuStyles.featSmallCover}>
                  <div style={stuStyles.featSmallFrame}>
                    <div style={stuStyles.featSmallNum}>№ {String(p.id).padStart(2, "0")}</div>
                    <div style={stuStyles.featSmallTitle}>{p.title}</div>
                    <div style={stuStyles.featSmallMeta}><span>{p.category}</span><span>{p.market}</span></div>
                  </div>
                </div>
                <div style={stuStyles.featSmallBody}>
                  <div style={stuStyles.featCat}>{p.category}</div>
                  <h4 style={stuStyles.featSmallH4}>{p.title}</h4>
                  <p style={stuStyles.featSmallBlurb}>{p.blurb}</p>
                  <div style={stuStyles.featFoot}>
                    <span style={stuStyles.featPrice}>{p.price}</span>
                    <span style={stuStyles.featLink}>{t("viewArrow")}</span>
                  </div>
                </div>
              </article>
            ))}
          </div>
        </div>
      </section>}

      {/* Catalog grid */}
      <section id="catalog" style={{...stuStyles.cat, ...(isMobile ? { padding: "40px 16px" } : isTablet ? { padding: "60px 32px" } : {})}}>
        <div style={{...stuStyles.catHead, ...(isMobile ? { flexDirection: "column", alignItems: "flex-start", gap: 8 } : {})}}>
          <div>
            <div style={stuStyles.kicker}>{t("theCatalog")}</div>
            <h2 style={{...stuStyles.catTitle, ...(isMobile ? { fontSize: 28 } : isTablet ? { fontSize: 36 } : {})}}>{t("everythingSorted")}</h2>
          </div>
          <div style={stuStyles.catCount}>
            <span style={stuStyles.catCountNum}>{filtered.length}</span>
            <span style={stuStyles.catCountLbl}>
              {t("titles")}{topic !== "All" && ` ${lang === "en" ? "in" : lang === "pt" ? "em" : "en"} ${topic}`}{market !== "ALL" && ` · ${activeMarketLabel}`}
            </span>
          </div>
        </div>

        <div style={stuStyles.filterStack}>
          <div style={{...stuStyles.filterRow, ...(isMobile ? { gridTemplateColumns: "1fr", gap: 8 } : {})}}>
            <div style={stuStyles.filterLabel}>{t("topic")}</div>
            <div style={{...stuStyles.tabs, ...(isMobile ? { overflowX: "auto", WebkitOverflowScrolling: "touch" } : {})}}>
              {topicFilters.map(f => (
                <button key={f} onClick={() => setTopic(f)}
                  style={{...stuStyles.tab, ...(topic === f ? stuStyles.tabActive : {})}}>{f}</button>
              ))}
            </div>
          </div>
          <div style={{...stuStyles.filterRow, ...(isMobile ? { gridTemplateColumns: "1fr", gap: 8 } : {})}}>
            <div style={stuStyles.filterLabel}>{t("market")}</div>
            <div style={{...stuStyles.tabs, ...(isMobile ? { overflowX: "auto", WebkitOverflowScrolling: "touch" } : {})}}>
              {marketFilters.map(m => (
                <button key={m.code} onClick={() => handleMarket(m.code)}
                  style={{...stuStyles.tab, ...stuStyles.tabMarket, ...(market === m.code ? stuStyles.tabActive : {})}}>
                  <span style={{...stuStyles.tabFlag, ...(market === m.code ? stuStyles.tabFlagActive : {})}}>{m.flag}</span>
                  <span>{m.label}</span>
                </button>
              ))}
            </div>
          </div>
        </div>

        <div style={{...stuStyles.grid, ...(isMobile ? { gridTemplateColumns: "1fr" } : isTablet ? { gridTemplateColumns: "repeat(2, 1fr)" } : {})}}>
          {filtered.map((p) => (
            <article key={p.id} style={stuStyles.card} onClick={() => onProduct(p.id)}>
              <div style={stuStyles.cardCover}>
                <div style={stuStyles.cardCoverInner}>
                  <div style={stuStyles.cardCoverCat}>{p.category}</div>
                  <div style={stuStyles.cardCoverTitle}>{p.title}</div>
                  <div style={stuStyles.cardCoverFoot}>
                    <span>№ {String(p.id).padStart(2, "0")}</span>
                    <span>{p.market}</span>
                  </div>
                </div>
              </div>
              <div style={stuStyles.cardBody}>
                <div style={stuStyles.cardTop}>
                  <div style={stuStyles.cardCat}>{p.category}</div>
                  <div style={stuStyles.cardMarket}>{p.market}</div>
                </div>
                <h3 style={stuStyles.cardTitle}>{p.title}</h3>
                <p style={stuStyles.cardBlurb}>{p.blurb}</p>
                <div style={stuStyles.cardFoot}>
                  <span style={stuStyles.cardPrice}>{p.price}</span>
                  <span style={stuStyles.cardCta}>{t("viewArrow")}</span>
                </div>
              </div>
            </article>
          ))}
        </div>
      </section>

      {/* About teaser + newsletter */}
      <section style={{...stuStyles.lower, ...(isMobile ? { gridTemplateColumns: "1fr", padding: "40px 16px" } : isTablet ? { gridTemplateColumns: "1fr", padding: "60px 32px" } : {})}}>
        <div style={{...stuStyles.aboutCard, ...(isMobile ? { padding: 24 } : {})}}>
          <div style={stuStyles.kicker}>About</div>
          <h2 style={stuStyles.aboutTitle}>One person. A catalog that grows quietly.</h2>
          <p style={stuStyles.aboutP}>
            I write short, practical books in three languages — English, Spanish, and
            Portuguese. Each one is meant to do one thing well, for one specific reader,
            for the price of a cup of coffee or two.
          </p>
          <p style={stuStyles.aboutP}>
            The catalog leans on what I'm doing in my own work: AI as a tool, not a
            religion; small systems that compound; useful over impressive.
          </p>
          <a style={stuStyles.aboutLink} onClick={onAbout}>Read the full note →</a>
        </div>
        <div style={stuStyles.subCard}>
          <div style={stuStyles.subKicker}>Notes by email</div>
          <h3 style={stuStyles.subTitle}>One short note. First Sunday of the month.</h3>
          <p style={stuStyles.subBody}>What I'm working on, one tool worth your attention, and the next book in the queue. Three paragraphs.</p>
          <div style={stuStyles.subForm}>
            <input style={stuStyles.subInput} placeholder="your@email.com" />
            <button style={stuStyles.subBtn}>Subscribe →</button>
          </div>
          <div style={stuStyles.subFine}>4,200 readers · One-click unsubscribe</div>
        </div>
      </section>
    </React.Fragment>
  );
};

/* ─── Product detail view ───────────────────────────────────── */

const StudioProduct = ({ product, products, onBack, onProduct, onCheckout, t, lang, isMobile, isTablet }) => {
  const p = product;
  const mob = isMobile;
  const tab = isTablet;

  const C = {
    cream: "#FAF7F2", ink: "#0B0B0B", terra: "#C8553D", brass: "#5d6e4c",
    fern: "#5B7553", slate: "#1a1f1c", rule: "#D9D2C4", creamDp: "#F2E5D0",
    gold: "#c09a40"
  };
  const F = {
    display: '"Fraunces", Georgia, serif',
    stamp: '"Inter", system-ui, sans-serif',
    body: '"Inter", system-ui, sans-serif',
    mono: '"JetBrains Mono", ui-monospace, monospace'
  };

  const sp = (window.SALES_PAGES && window.SALES_PAGES[p.id]) || null;

  const related = products
    .filter(x => x.id !== p.id && (x.category === p.category || x.market === p.market))
    .slice(0, 3);

  const chapters = p.chapters || [
    "Introduction", "Setup", "The core method", "Templates",
    "Common pitfalls", "Implementation plan", "Next steps"
  ];

  /* ── Countdown timer state ── */
  const [countdown, setCountdown] = React.useState("23:59:59");
  React.useEffect(() => {
    if (!sp) return;
    const tick = () => {
      const now = new Date();
      const end = new Date();
      end.setHours(23, 59, 59, 0);
      let diff = Math.max(0, Math.floor((end - now) / 1000));
      const hh = String(Math.floor(diff / 3600)).padStart(2, "0");
      diff %= 3600;
      const mm = String(Math.floor(diff / 60)).padStart(2, "0");
      const ss = String(diff % 60).padStart(2, "0");
      setCountdown(`${hh}:${mm}:${ss}`);
    };
    tick();
    const iv = setInterval(tick, 1000);
    return () => clearInterval(iv);
  }, [sp]);

  /* ── FAQ accordion state ── */
  const [openFaq, setOpenFaq] = React.useState(0);

  /* ══════════════════════════════════════════════════
     FALLBACK — no sales page data
  ══════════════════════════════════════════════════ */
  if (!sp) {
    return (
      <React.Fragment>
        <div style={{ ...stuStyles.crumb, ...(mob ? { padding: "20px 16px 0", flexWrap: "wrap" } : tab ? { padding: "24px 32px 0" } : {}) }}>
          <a style={stuStyles.crumbLink} onClick={onBack}>← {t("catalog")}</a>
          <span style={stuStyles.crumbSep}>/</span>
          <span style={stuStyles.crumbCat}>{p.category}</span>
          {!mob && <span style={stuStyles.crumbSep}>/</span>}
          {!mob && <span style={stuStyles.crumbCur}>{p.title}</span>}
        </div>

        <section style={{ padding: mob ? "24px 16px 48px" : tab ? "32px 32px 64px" : "48px 64px 80px", background: C.cream }}>
          <h1 style={{ fontFamily: F.display, fontSize: mob ? 32 : tab ? 40 : 52, fontWeight: 900, color: C.ink, marginBottom: 12 }}>{p.title}</h1>
          <p style={{ fontFamily: F.body, fontSize: 18, color: C.slate, marginBottom: 28, lineHeight: 1.6 }}>{p.blurb}</p>
          <div style={{ display: "flex", alignItems: "center", gap: 20, marginBottom: 40 }}>
            <span style={{ fontFamily: F.stamp, fontSize: 32, color: C.ink }}>{p.price}</span>
            <a style={{ background: C.terra, color: C.cream, fontFamily: F.stamp, fontSize: 16, padding: "14px 32px", borderRadius: 9999, cursor: "pointer", textDecoration: "none" }} onClick={() => onCheckout?.(p.id)}>{t("buyArrow")}</a>
          </div>
          <ol style={{ listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 12 }}>
            {chapters.map((c, i) => (
              <li key={i} style={{ display: "flex", gap: 16, alignItems: "flex-start", padding: "12px 0", borderBottom: `1px solid ${C.rule}` }}>
                <span style={{ fontFamily: F.mono, fontSize: 13, color: C.terra, minWidth: 28 }}>{String(i + 1).padStart(2, "0")}</span>
                <span style={{ fontFamily: F.body, fontSize: 16, color: C.slate }}>{c}</span>
              </li>
            ))}
          </ol>
        </section>

        {related.length > 0 && (
          <section style={{ ...stuStyles.pdRelated, ...(mob ? { padding: "40px 16px 60px" } : tab ? { padding: "48px 32px 80px" } : {}) }}>
            <div style={stuStyles.kicker}>{t("readNext")}</div>
            <h2 style={{ ...stuStyles.pdSectionTitle, ...(mob ? { fontSize: 28 } : tab ? { fontSize: 36 } : {}) }}>{t("fromSameShelf")}</h2>
            <div style={{ ...stuStyles.grid, ...(mob ? { gridTemplateColumns: "1fr" } : tab ? { gridTemplateColumns: "repeat(2, 1fr)" } : {}) }}>
              {related.map(r => (
                <article key={r.id} style={stuStyles.card} onClick={() => onProduct(r.id)}>
                  <div style={stuStyles.cardCover}><div style={stuStyles.cardCoverInner}>
                    <div style={stuStyles.cardCoverCat}>{r.category}</div>
                    <div style={stuStyles.cardCoverTitle}>{r.title}</div>
                    <div style={stuStyles.cardCoverFoot}><span>№ {String(r.id).padStart(2, "0")}</span><span>{r.market}</span></div>
                  </div></div>
                  <div style={stuStyles.cardBody}>
                    <div style={stuStyles.cardTop}><div style={stuStyles.cardCat}>{r.category}</div><div style={stuStyles.cardMarket}>{r.market}</div></div>
                    <h3 style={stuStyles.cardTitle}>{r.title}</h3>
                    <p style={stuStyles.cardBlurb}>{r.blurb}</p>
                    <div style={stuStyles.cardFoot}><span style={stuStyles.cardPrice}>{r.price}</span><span style={stuStyles.cardCta}>View →</span></div>
                  </div>
                </article>
              ))}
            </div>
          </section>
        )}
      </React.Fragment>
    );
  }

  /* ══════════════════════════════════════════════════
     FULL SALES PAGE
  ══════════════════════════════════════════════════ */
  const hPad = mob ? "0 16px" : tab ? "0 32px" : "0 64px";
  const secPad = mob ? "48px 16px" : tab ? "64px 32px" : "80px 64px";

  return (
    <React.Fragment>

      {/* ── 1. Urgency bar ── */}
      <div style={{
        position: "sticky", top: 0, zIndex: 100,
        background: C.ink, borderBottom: `4px solid ${C.brass}`,
        display: "flex", alignItems: "center", justifyContent: "center", gap: mob ? 8 : 20,
        padding: mob ? "10px 16px" : "12px 32px", flexWrap: "wrap"
      }}>
        <span style={{ fontFamily: F.stamp, fontSize: mob ? 11 : 13, color: C.brass, letterSpacing: 1, textTransform: "uppercase" }}>In force today</span>
        <span style={{ fontFamily: F.body, fontSize: mob ? 12 : 14, color: C.cream }}>·  Launch price {p.price}  ·</span>
        <span style={{ fontFamily: F.mono, fontSize: mob ? 14 : 16, color: C.brass, letterSpacing: 2 }}>{countdown}</span>
      </div>

      {/* ── 2. Hero ── */}
      <section style={{ background: C.cream, padding: mob ? "48px 16px 56px" : tab ? "64px 32px 72px" : "80px 64px 96px" }}>
        <div style={{
          display: "grid",
          gridTemplateColumns: mob ? "1fr" : tab ? "1fr" : "1fr 420px",
          gap: mob ? 48 : tab ? 56 : 80,
          maxWidth: 1140, margin: "0 auto", alignItems: "center"
        }}>
          {/* Left: copy */}
          <div>
            <h1 style={{
              fontFamily: F.display, fontWeight: 900,
              fontSize: mob ? 36 : tab ? 48 : 62,
              lineHeight: 1.08, letterSpacing: -1.5, color: C.ink,
              margin: "0 0 8px 0"
            }}>
              {sp.hero.h1}{" "}
              <em style={{ fontStyle: "italic", color: C.terra }}>{sp.hero.h1em}</em>
            </h1>

            <p style={{ fontFamily: F.body, fontSize: mob ? 17 : 19, color: C.slate, lineHeight: 1.65, margin: "24px 0 32px", maxWidth: 560 }}>{sp.hero.lede}</p>

            <ul style={{ listStyle: "none", padding: 0, margin: "0 0 36px", display: "grid", gap: 12 }}>
              {(sp.hero.bullets || []).map((b, i) => (
                <li key={i} style={{ display: "flex", alignItems: "flex-start", gap: 12, fontFamily: F.body, fontSize: mob ? 15 : 17, color: C.ink }}>
                  <span style={{ color: C.fern, fontWeight: 700, flexShrink: 0, marginTop: 2 }}>✓</span>
                  {b}
                </li>
              ))}
            </ul>

            <div style={{ display: "flex", flexDirection: "column", gap: 12, alignItems: mob ? "stretch" : "flex-start" }}>
              <a
                style={{
                  background: C.terra, color: C.cream, fontFamily: F.stamp,
                  fontSize: mob ? 16 : 18, padding: mob ? "16px 28px" : "18px 44px",
                  borderRadius: 9999, cursor: "pointer", textDecoration: "none",
                  textAlign: "center", letterSpacing: 0.5, display: "block"
                }}
                onClick={() => onCheckout?.(p.id)}
              >{sp.hero.ctaText || "Get Instant Access"}</a>
              <span style={{ fontFamily: F.body, fontSize: 13, color: C.slate, textAlign: mob ? "center" : "left" }}>{sp.hero.trustLine}</span>
            </div>
          </div>

          {/* Right: ebook cover image (desktop) / Below hero text (mobile) */}
          <div style={{ display: "flex", justifyContent: "center", order: mob ? 1 : 0, marginTop: mob ? 8 : 0 }}>
            <img
              src={p.coverImg || "/covers/" + p.id + ".webp"}
              alt={p.title}
              style={{
                width: mob ? 220 : tab ? 320 : 420,
                height: "auto",
                borderRadius: 6,
                boxShadow: "8px 12px 40px rgba(0,0,0,0.30), 0 2px 8px rgba(0,0,0,0.12)"
              }}
            />
          </div>
        </div>
      </section>

      {/* ── 3. Trust bar ── */}
      {sp.trustStats && sp.trustStats.length > 0 && (
        <div style={{
          background: C.ink, borderTop: `4px solid ${C.brass}`, borderBottom: `4px solid ${C.brass}`,
          padding: mob ? "28px 16px" : "36px 64px"
        }}>
          <div style={{
            display: "grid",
            gridTemplateColumns: mob ? "repeat(2, 1fr)" : tab ? "repeat(2, 1fr)" : `repeat(${sp.trustStats.length}, 1fr)`,
            gap: mob ? 24 : 0, maxWidth: 900, margin: "0 auto"
          }}>
            {sp.trustStats.map((st, i) => (
              <div key={i} style={{
                textAlign: "center",
                borderRight: (!mob && i < sp.trustStats.length - 1) ? `1px solid ${C.slate}` : "none",
                padding: mob ? 0 : "0 32px"
              }}>
                <div style={{ fontFamily: F.mono, fontSize: mob ? 26 : 32, color: C.gold, fontWeight: 700, letterSpacing: -1 }}>{st.value}</div>
                <div style={{ fontFamily: F.body, fontSize: mob ? 12 : 13, color: C.rule, textTransform: "uppercase", letterSpacing: 1, marginTop: 4 }}>{st.label}</div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* ── 4. Problem amplification ── */}
      {sp.pain && (
        <section style={{ background: C.cream, padding: secPad }}>
          <div style={{ maxWidth: 960, margin: "0 auto" }}>
            <div style={{ textAlign: "center", marginBottom: 48 }}>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>{sp.pain.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 30 : tab ? 38 : 46, color: C.ink, margin: "0 0 16px", lineHeight: 1.1 }}>{sp.pain.h2}</h2>
              <p style={{ fontFamily: F.body, fontSize: mob ? 16 : 18, color: C.slate, lineHeight: 1.65, maxWidth: 600, margin: "0 auto" }}>{sp.pain.lede}</p>
            </div>
            <div style={{
              display: "grid",
              gridTemplateColumns: mob ? "1fr" : tab ? "repeat(2, 1fr)" : "repeat(3, 1fr)",
              gap: 24
            }}>
              {(sp.pain.stories || []).map((s, i) => (
                <div key={i} style={{
                  background: "#fff", borderRadius: 16, border: `1px solid ${C.creamDp}`,
                  padding: mob ? "28px 20px" : "32px 28px"
                }}>
                  <div style={{ fontFamily: F.stamp, fontSize: 15, color: C.ink, marginBottom: 12 }}>{s.title}</div>
                  <p style={{ fontFamily: F.body, fontSize: 15, color: C.slate, lineHeight: 1.65, margin: 0 }}>{s.body}</p>
                </div>
              ))}
            </div>
          </div>
        </section>
      )}

      {/* ── 5. Solution reveal ── */}
      {sp.solution && (
        <section style={{ background: C.slate, padding: secPad }}>
          <div style={{ maxWidth: 960, margin: "0 auto" }}>
            <div style={{ textAlign: "center", marginBottom: 56 }}>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.brass, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>{sp.solution.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 30 : tab ? 38 : 46, color: C.cream, margin: "0 0 16px", lineHeight: 1.1 }}>{sp.solution.h2}</h2>
              <p style={{ fontFamily: F.body, fontSize: mob ? 16 : 18, color: C.rule, lineHeight: 1.65, maxWidth: 600, margin: "0 auto" }}>{sp.solution.lede}</p>
            </div>
            {/* 3-step visual summary strip */}
            <div style={{
              display: "flex", justifyContent: "center", alignItems: "center",
              gap: mob ? 12 : 24, flexWrap: "wrap",
              margin: "0 auto 48px", maxWidth: 700,
              padding: "20px 24px", borderRadius: 12,
              background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.08)"
            }}>
              {(sp.solution.items || []).map((it, i, arr) => (
                <React.Fragment key={i}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                    <span style={{
                      fontFamily: F.mono, fontSize: 11, color: C.brass,
                      background: "rgba(192,154,64,0.15)", borderRadius: 6,
                      padding: "4px 8px", fontWeight: 700
                    }}>{it.num || String(i + 1).padStart(2, "0")}</span>
                    <span style={{ fontFamily: F.stamp, fontSize: mob ? 12 : 14, color: C.cream, fontWeight: 600 }}>{it.title}</span>
                  </div>
                  {i < arr.length - 1 && (
                    <span style={{ fontFamily: F.body, fontSize: 16, color: C.gold, flexShrink: 0 }}>{mob ? "" : "→"}</span>
                  )}
                </React.Fragment>
              ))}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "repeat(3, 1fr)", gap: 32 }}>
              {(sp.solution.items || []).map((it, i) => (
                <div key={i} style={{ padding: mob ? "0 0 32px" : 0, borderBottom: mob && i < sp.solution.items.length - 1 ? `1px solid ${C.ink}` : "none" }}>
                  <div style={{ fontFamily: F.stamp, fontSize: 64, color: C.gold, lineHeight: 1, marginBottom: 16, fontWeight: 700 }}>{it.num || String(i + 1).padStart(2, "0")}</div>
                  <div style={{ fontFamily: F.display, fontWeight: 700, fontSize: 20, color: C.cream, marginBottom: 10 }}>{it.title}</div>
                  <p style={{ fontFamily: F.body, fontSize: 15, color: C.rule, lineHeight: 1.65, margin: 0 }}>{it.body}</p>
                </div>
              ))}
            </div>
          </div>
        </section>
      )}

      {/* ── 6. Chapters ── */}
      <section style={{ background: C.cream, padding: secPad }}>
        <div style={{ maxWidth: 960, margin: "0 auto" }}>
          <div style={{ textAlign: "center", marginBottom: 48 }}>
            <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>What's Inside</div>
            <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 28 : tab ? 36 : 44, color: C.ink, margin: 0, lineHeight: 1.1 }}>{chapters.length} chapters · one clear path</h2>
          </div>
          <div style={{
            display: "grid",
            gridTemplateColumns: mob ? "1fr" : tab ? "repeat(2, 1fr)" : "repeat(3, 1fr)",
            gap: 16
          }}>
            {chapters.map((c, i) => (
              <div key={i} style={{
                background: "#fff", borderRadius: 12, border: `1px solid ${C.rule}`,
                padding: "20px 24px", display: "flex", gap: 16, alignItems: "flex-start"
              }}>
                <span style={{ fontFamily: F.mono, fontSize: 13, color: C.terra, fontWeight: 700, flexShrink: 0, marginTop: 2 }}>{String(i + 1).padStart(2, "0")}</span>
                <span style={{ fontFamily: F.body, fontSize: 15, color: C.slate, lineHeight: 1.5 }}>{c}</span>
              </div>
            ))}
          </div>
        </div>
      </section>

      {/* ── 7. Testimonials ── */}
      {sp.testimonials && (
        <section style={{ background: C.slate, padding: secPad }}>
          <div style={{ maxWidth: 1060, margin: "0 auto" }}>
            <div style={{ textAlign: "center", marginBottom: 52 }}>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.brass, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>{sp.testimonials.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 28 : tab ? 36 : 44, color: C.cream, margin: "0 0 12px", lineHeight: 1.1 }}>{sp.testimonials.h2}</h2>
              {sp.testimonials.sub && <p style={{ fontFamily: F.body, fontSize: 17, color: C.rule }}>{sp.testimonials.sub}</p>}
            </div>
            <div style={{
              display: "grid",
              gridTemplateColumns: mob ? "1fr" : tab ? "repeat(2, 1fr)" : "repeat(3, 1fr)",
              gap: 20
            }}>
              {(sp.testimonials.items || []).map((tm, i) => (
                <div key={i} style={{ background: C.ink, borderRadius: 16, padding: "28px 24px", border: `1px solid #1a1a1a` }}>
                  <div style={{ color: C.gold, fontSize: 16, marginBottom: 14, letterSpacing: 2 }}>{"★".repeat(tm.stars || 5)}</div>
                  <p style={{ fontFamily: F.body, fontSize: 15, color: C.cream, lineHeight: 1.7, margin: "0 0 20px", fontStyle: "italic" }}>"{tm.quote}"</p>
                  <div>
                    <div style={{ fontFamily: F.stamp, fontSize: 13, color: C.cream }}>{tm.name}</div>
                    <div style={{ fontFamily: F.body, fontSize: 12, color: C.rule, marginTop: 2 }}>{tm.detail}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </section>
      )}

      {/* ── 8. Bonuses ── */}
      {sp.bonuses && (
        <section style={{ background: C.creamDp, padding: secPad }}>
          <div style={{ maxWidth: 960, margin: "0 auto" }}>
            <div style={{ textAlign: "center", marginBottom: 48 }}>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>{sp.bonuses.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 28 : tab ? 36 : 44, color: C.ink, margin: "0 0 12px", lineHeight: 1.1 }}>{sp.bonuses.h2}</h2>
              {sp.bonuses.sub && <p style={{ fontFamily: F.body, fontSize: 17, color: C.slate }}>{sp.bonuses.sub}</p>}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "repeat(3, 1fr)", gap: 24 }}>
              {(sp.bonuses.items || []).map((b, i) => (
                <div key={i} style={{ background: "#fff", borderRadius: 16, padding: "28px 24px", boxShadow: "0 2px 12px rgba(0,0,0,0.06)" }}>
                  <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 2, textTransform: "uppercase", marginBottom: 8 }}>Bonus {b.num || i + 1}</div>
                  <div style={{ fontFamily: F.display, fontWeight: 700, fontSize: 19, color: C.ink, marginBottom: 10 }}>{b.title}</div>
                  <p style={{ fontFamily: F.body, fontSize: 14, color: C.slate, lineHeight: 1.6, margin: "0 0 16px" }}>{b.desc}</p>
                  <div style={{ fontFamily: F.mono, fontSize: 13, color: C.fern, fontWeight: 700 }}>{b.value}</div>
                </div>
              ))}
            </div>
            {/* Total stacked value */}
            {(() => {
              const vals = (sp.bonuses.items || []).map(b => parseFloat((b.value || "").replace(/[^0-9.]/g, ""))).filter(v => !isNaN(v));
              const total = vals.reduce((s, v) => s + v, 0);
              return total > 0 ? (
                <div style={{
                  textAlign: "center", marginTop: 36, padding: "20px 24px",
                  background: "#fff", borderRadius: 12,
                  border: "2px dashed " + C.fern, maxWidth: 480, margin: "36px auto 0"
                }}>
                  <div style={{ fontFamily: F.stamp, fontSize: 13, color: C.slate, marginBottom: 4 }}>
                    Total bonus value
                  </div>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 12 }}>
                    <span style={{ fontFamily: F.display, fontSize: 32, fontWeight: 900, color: C.fern }}>
                      {"£"}{total.toFixed(0)}
                    </span>
                    <span style={{ fontFamily: F.body, fontSize: 15, color: C.slate }}>
                      included at no extra cost
                    </span>
                  </div>
                </div>
              ) : null;
            })()}
          </div>
        </section>
      )}

      {/* ── 9. Buy CTA ── */}
      <section style={{ background: C.cream, padding: secPad }}>
        <div style={{ maxWidth: 560, margin: "0 auto", textAlign: "center" }}>
          <div style={{
            background: "#fff", borderRadius: 24, boxShadow: "0 8px 48px rgba(0,0,0,0.12)",
            padding: mob ? "36px 24px" : "48px 40px"
          }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 28, paddingBottom: 20, borderBottom: `1px solid ${C.rule}` }}>
              <div style={{ fontFamily: F.body, fontSize: 15, color: C.slate, textAlign: "left", maxWidth: "60%" }}>{p.title}</div>
              <div style={{ fontFamily: F.stamp, fontSize: 26, color: C.ink }}>{p.price}</div>
            </div>
            <a
              style={{
                display: "block", background: C.terra, color: C.cream,
                fontFamily: F.stamp, fontSize: mob ? 16 : 18,
                padding: "18px 32px", borderRadius: 9999, cursor: "pointer",
                textDecoration: "none", marginBottom: 24, letterSpacing: 0.5
              }}
              onClick={() => onCheckout?.(p.id)}
            >{sp.finalCta ? sp.finalCta.ctaText : "Get Instant Access"}</a>
            <div style={{ display: "flex", justifyContent: "center", gap: 28, flexWrap: "wrap" }}>
              {[["🔒", "Secure checkout"], ["📧", "Instant delivery"], ["↩", "14-day refund"]].map(([ic, lb]) => (
                <div key={lb} style={{ fontFamily: F.body, fontSize: 12, color: C.slate, display: "flex", alignItems: "center", gap: 6 }}>
                  <span>{ic}</span><span>{lb}</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </section>

      {/* ── 10. Guarantee ── */}
      {sp.guarantee && (
        <section style={{
          background: C.ink, borderTop: `4px solid ${C.brass}`, borderBottom: `4px solid ${C.brass}`,
          padding: secPad
        }}>
          <div style={{
            maxWidth: 900, margin: "0 auto",
            display: "grid", gridTemplateColumns: mob ? "1fr" : "auto 1fr",
            gap: mob ? 36 : 64, alignItems: "center"
          }}>
            <div style={{ display: "flex", justifyContent: mob ? "center" : "flex-start" }}>
              <div style={{
                width: 140, height: 140, borderRadius: "50%",
                background: C.terra, display: "flex", flexDirection: "column",
                alignItems: "center", justifyContent: "center",
                transform: "rotate(-6deg)", boxShadow: "0 4px 24px rgba(200,85,61,0.4)"
              }}>
                <div style={{ fontFamily: F.stamp, fontSize: 20, color: C.cream, lineHeight: 1.1, textAlign: "center" }}>{sp.guarantee.days}</div>
                <div style={{ fontFamily: F.stamp, fontSize: 9, color: C.cream, letterSpacing: 1.5, textTransform: "uppercase", marginTop: 4 }}>DAY</div>
                <div style={{ fontFamily: F.body, fontSize: 9, color: C.creamDp, textAlign: "center", lineHeight: 1.3 }}>NO QUESTIONS<br/>REFUND</div>
              </div>
            </div>
            <div>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.brass, letterSpacing: 3, textTransform: "uppercase", marginBottom: 10 }}>{sp.guarantee.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 26 : 34, color: C.cream, margin: "0 0 16px", lineHeight: 1.1 }}>{sp.guarantee.h2}</h2>
              <p style={{ fontFamily: F.body, fontSize: 16, color: C.rule, lineHeight: 1.7, margin: 0 }}>{sp.guarantee.body}</p>
            </div>
          </div>
        </section>
      )}

      {/* ── 10b. Price comparison ── */}
      {sp.trustStats && (
        <section style={{ background: C.creamDp, padding: mob ? "48px 16px" : "64px 32px" }}>
          <div style={{ maxWidth: 800, margin: "0 auto", textAlign: "center" }}>
            <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>
              Three ways to navigate the Act
            </div>
            <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 26 : 36, color: C.ink, margin: "0 0 36px", lineHeight: 1.1 }}>
              Compare your options
            </h2>
            <div style={{
              display: "grid",
              gridTemplateColumns: mob ? "1fr" : "repeat(3, 1fr)",
              gap: 16
            }}>
              {/* Option 1: Solicitor */}
              <div style={{
                background: "#fff", borderRadius: 16, padding: "28px 20px",
                border: "1px solid " + C.rule, opacity: 0.7
              }}>
                <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.slate, letterSpacing: 2, textTransform: "uppercase", marginBottom: 12 }}>Housing solicitor</div>
                <div style={{ fontFamily: F.display, fontSize: 32, fontWeight: 900, color: C.ink, marginBottom: 8 }}>{"£200–500"}</div>
                <div style={{ fontFamily: F.body, fontSize: 13, color: C.slate, lineHeight: 1.5 }}>Per hour. One letter. No templates you keep. Clock starts again next time.</div>
              </div>
              {/* Option 2: Free advice */}
              <div style={{
                background: "#fff", borderRadius: 16, padding: "28px 20px",
                border: "1px solid " + C.rule, opacity: 0.7
              }}>
                <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.slate, letterSpacing: 2, textTransform: "uppercase", marginBottom: 12 }}>Free online advice</div>
                <div style={{ fontFamily: F.display, fontSize: 32, fontWeight: 900, color: C.ink, marginBottom: 8 }}>{"£0"}</div>
                <div style={{ fontFamily: F.body, fontSize: 13, color: C.slate, lineHeight: 1.5 }}>Scattered across Reddit, Shelter, gov.uk. No templates. No tribunal walkthrough. Hours of searching.</div>
              </div>
              {/* Option 3: This guide — highlighted */}
              <div style={{
                background: "#fff", borderRadius: 16, padding: "28px 20px",
                border: "2px solid " + C.terra,
                boxShadow: "0 4px 24px rgba(200,85,61,0.12)"
              }}>
                <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 2, textTransform: "uppercase", marginBottom: 12 }}>This guide</div>
                <div style={{ fontFamily: F.display, fontSize: 32, fontWeight: 900, color: C.terra, marginBottom: 8 }}>{p.price}</div>
                <div style={{ fontFamily: F.body, fontSize: 13, color: C.slate, lineHeight: 1.5 }}>One-time. 35 pages + 8 templates + tribunal walkthrough. 14-day refund. Yours forever.</div>
              </div>
            </div>
          </div>
        </section>
      )}

      {/* ── 11. FAQ ── */}
      {sp.faq && (
        <section style={{ background: C.cream, padding: secPad }}>
          <div style={{ maxWidth: 720, margin: "0 auto" }}>
            <div style={{ textAlign: "center", marginBottom: 48 }}>
              <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.terra, letterSpacing: 3, textTransform: "uppercase", marginBottom: 12 }}>{sp.faq.kicker}</div>
              <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 28 : tab ? 36 : 44, color: C.ink, margin: 0, lineHeight: 1.1 }}>{sp.faq.h2}</h2>
            </div>
            <div style={{ display: "grid", gap: 12 }}>
              {(sp.faq.items || []).map((item, i) => (
                <div key={i} style={{ background: "#fff", borderRadius: 12, border: `1px solid ${C.rule}`, overflow: "hidden" }}>
                  <button
                    onClick={() => setOpenFaq(openFaq === i ? -1 : i)}
                    style={{
                      width: "100%", display: "flex", justifyContent: "space-between", alignItems: "center",
                      padding: "18px 24px", background: "transparent", border: "none", cursor: "pointer",
                      fontFamily: F.body, fontSize: 16, fontWeight: 600, color: C.ink, textAlign: "left", gap: 16
                    }}
                  >
                    <span>{item.q}</span>
                    <span style={{ flexShrink: 0, transition: "transform 0.2s", transform: openFaq === i ? "rotate(180deg)" : "rotate(0deg)", color: C.terra, fontSize: 18 }}>⌄</span>
                  </button>
                  {openFaq === i && (
                    <div style={{ padding: "0 24px 20px", fontFamily: F.body, fontSize: 15, color: C.slate, lineHeight: 1.7 }}>{item.a}</div>
                  )}
                </div>
              ))}
            </div>
          </div>
        </section>
      )}

      {/* ── 12. Final CTA ── */}
      {sp.finalCta && (
        <section style={{ background: C.terra, padding: secPad }}>
          <div style={{ maxWidth: 720, margin: "0 auto", textAlign: "center" }}>
            <div style={{ fontFamily: F.stamp, fontSize: 11, color: C.creamDp, letterSpacing: 3, textTransform: "uppercase", marginBottom: 16, opacity: 0.85 }}>{sp.finalCta.kicker}</div>
            <h2 style={{ fontFamily: F.display, fontWeight: 900, fontSize: mob ? 32 : tab ? 42 : 52, color: C.cream, margin: "0 0 20px", lineHeight: 1.08 }}>{sp.finalCta.h2}</h2>
            <p style={{ fontFamily: F.body, fontSize: mob ? 16 : 18, color: C.creamDp, lineHeight: 1.7, margin: "0 0 40px", maxWidth: 560, marginLeft: "auto", marginRight: "auto" }}>{sp.finalCta.body}</p>
            <a
              style={{
                display: "inline-block", background: C.cream, color: C.terra,
                fontFamily: F.stamp, fontSize: mob ? 16 : 18,
                padding: mob ? "16px 32px" : "20px 52px", borderRadius: 9999,
                cursor: "pointer", textDecoration: "none", marginBottom: 20, letterSpacing: 0.5
              }}
              onClick={() => onCheckout?.(p.id)}
            >{sp.finalCta.ctaText}</a>
            <div style={{ fontFamily: F.body, fontSize: 13, color: C.creamDp, opacity: 0.8 }}>{sp.finalCta.trustLine}</div>
          </div>
        </section>
      )}

      {/* ── 13. Related products ── */}
      {related.length > 0 && (
        <section style={{ ...stuStyles.pdRelated, ...(mob ? { padding: "40px 16px 60px" } : tab ? { padding: "48px 32px 80px" } : {}) }}>
          <div style={stuStyles.kicker}>{t("readNext")}</div>
          <h2 style={{ ...stuStyles.pdSectionTitle, ...(mob ? { fontSize: 28 } : tab ? { fontSize: 36 } : {}) }}>{t("fromSameShelf")}</h2>
          <div style={{ ...stuStyles.grid, ...(mob ? { gridTemplateColumns: "1fr" } : tab ? { gridTemplateColumns: "repeat(2, 1fr)" } : {}) }}>
            {related.map(r => (
              <article key={r.id} style={stuStyles.card} onClick={() => onProduct(r.id)}>
                <div style={stuStyles.cardCover}><div style={stuStyles.cardCoverInner}>
                  <div style={stuStyles.cardCoverCat}>{r.category}</div>
                  <div style={stuStyles.cardCoverTitle}>{r.title}</div>
                  <div style={stuStyles.cardCoverFoot}><span>№ {String(r.id).padStart(2, "0")}</span><span>{r.market}</span></div>
                </div></div>
                <div style={stuStyles.cardBody}>
                  <div style={stuStyles.cardTop}><div style={stuStyles.cardCat}>{r.category}</div><div style={stuStyles.cardMarket}>{r.market}</div></div>
                  <h3 style={stuStyles.cardTitle}>{r.title}</h3>
                  <p style={stuStyles.cardBlurb}>{r.blurb}</p>
                  <div style={stuStyles.cardFoot}><span style={stuStyles.cardPrice}>{r.price}</span><span style={stuStyles.cardCta}>View →</span></div>
                </div>
              </article>
            ))}
          </div>
        </section>
      )}

    </React.Fragment>
  );
};

/* ─── Legal pages (Privacy / Terms / Refunds) ──────────────── */

const LEGAL_CONTENT = {
  privacy: {
    title: "Privacy Policy",
    updated: "13 May 2026",
    sections: [
      { h: "Data controller", p: 'This site is operated by Alvaro Abreu ("we", "us"), an independent digital publisher. CNPJ pending registration. Registered address: São Paulo, SP, Brazil. Data protection contact: emaildoalvaroabreu@gmail.com. This policy complies with the Brazilian General Data Protection Law (Lei Geral de Proteção de Dados — LGPD, Law No. 13.709/2018), the UK GDPR, and the EU GDPR where applicable.' },
      { h: "What data we collect and why", p: "We collect only the minimum data required to fulfil your purchase. (1) Email address — to deliver the digital product and send your receipt. Legal basis: performance of a contract (LGPD Art. 7, V). (2) Payment details (card number, billing info) — collected and processed exclusively by our payment processor; we never see or store these. Legal basis: performance of a contract. We do not collect names, physical addresses, identity documents, IP addresses, or any biometric or sensitive data." },
      { h: "Analytics", p: "We use Plausible Analytics (Plausible Insights OÜ, Estonia, EU). Plausible is a privacy-first, cookie-free analytics tool. It does not use cookies, does not collect personal data, does not track users across sites, and does not store IP addresses. All metrics are aggregate and anonymous. No consent banner is required because no personal data is processed. More information: plausible.io/data-policy." },
      { h: "Cookies and local storage", p: "This site does not set any cookies. The only use of browser local storage is to remember your colour theme preference (light/dark mode). This is a technical necessity that does not involve personal data." },
      { h: "Payment processing", p: "Payments are handled by Kiwify Plataforma de Cursos Ltda (Brazil) and, for select products, Stripe Inc. (United States). These processors collect your payment information directly — we never see or store your full card number. Both comply with PCI-DSS Level 1 standards. By completing a purchase, you also agree to the privacy policies of the respective payment processor." },
      { h: "Third-party services", p: "The complete list of third-party services that may receive data when you visit this site: (1) Plausible Analytics (EU) — anonymous, aggregate traffic data only. (2) Kiwify (Brazil) — payment and email for product delivery. (3) Stripe (US) — payment processing for select products. (4) Vercel Inc. (US) — website hosting; may process server access logs including IP addresses under their own privacy policy. We do not use Google Analytics, Meta Pixel, advertising trackers, or any retargeting services. All fonts and scripts are self-hosted — no data is sent to Google, CDN providers, or other third parties when you browse this site." },
      { h: "International data transfers", p: "Your data may be processed in the following jurisdictions: Brazil (Kiwify), United States (Stripe, Vercel), and the European Union (Plausible). Where data is transferred outside your country of residence, it is protected by the service provider\'s contractual safeguards and applicable data protection frameworks. Under the LGPD (Art. 33), international transfers are permitted when the recipient country provides an adequate level of protection or when the transfer is necessary for the performance of a contract." },
      { h: "Your rights under the LGPD", p: "Under Brazil\'s LGPD (Art. 18), you have the right to: (a) confirm whether your data is being processed; (b) access your data; (c) correct incomplete or inaccurate data; (d) request anonymisation, blocking, or deletion of unnecessary data; (e) request data portability; (f) request deletion of data processed with your consent; (g) obtain information about which entities your data has been shared with; (h) be informed about the possibility of denying consent and the consequences; (i) revoke consent at any time. To exercise any of these rights, email emaildoalvaroabreu@gmail.com. We will respond within 15 days." },
      { h: "Your rights under GDPR (UK/EU visitors)", p: "If you are located in the UK or EU, you also have the right to lodge a complaint with your local data protection authority (ICO in the UK, or the relevant DPA in your EU member state). Our legal basis for processing purchase data is performance of a contract (GDPR Art. 6(1)(b))." },
      { h: "Data retention", p: "Purchase records (email, transaction ID, product purchased, date) are retained for 5 years for accounting and tax purposes as required by Brazilian law. You may request deletion of any data not required for legal retention at any time. Marketing-related data, if any, is deleted immediately upon request." },
      { h: "Children\'s privacy", p: "This site is not directed at children under 18. We do not knowingly collect data from minors. If you believe a minor has provided us with personal data, please contact us and we will delete it promptly." },
      { h: "Data security", p: "We implement appropriate technical and organisational measures to protect your data, including HTTPS encryption for all connections, Content Security Policy headers, and reliance on PCI-DSS compliant payment processors. This site does not store any personal data on its own servers." },
      { h: "Supervisory authority", p: "The Brazilian data protection authority is the Autoridade Nacional de Proteção de Dados (ANPD). Website: gov.br/anpd. You may file a complaint with the ANPD if you believe your data protection rights have been violated." },
      { h: "Changes to this policy", p: "We may update this policy to reflect changes in our practices or applicable law. The date at the top of this page reflects the latest revision. Material changes will be highlighted on the site. If you have made a purchase, we may notify you by email of significant changes." }
    ]
  },
  terms: {
    title: "Terms of Service",
    updated: "12 May 2026",
    sections: [
      { h: "Agreement", p: "By purchasing a digital product from this site, you agree to these terms. If you do not agree, please do not complete the purchase." },
      { h: "Digital products", p: "All products sold are digital downloads (PDF ebooks). Upon successful payment, you will receive access to download the file. Products are for personal use only." },
      { h: "Intellectual property", p: "All content, text, and designs in our ebooks are copyrighted by Alvaro Abreu. You may not reproduce, distribute, resell, or publicly display any part of the purchased materials without written permission." },
      { h: "Pricing and payment", p: "Prices are displayed in the local currency of each market (GBP, USD, EUR, AUD, BRL). All prices include applicable taxes where required. Prices may change without notice, but existing purchases are not affected." },
      { h: "Delivery", p: "Digital products are delivered instantly via download link after payment confirmation. If you experience any issues accessing your purchase, contact emaildoalvaroabreu@gmail.com." },
      { h: "Account", p: "No account creation is required. Purchase access is tied to your email address and the payment confirmation from our processor." },
      { h: "Limitation of liability", p: "Our ebooks provide general information and guidance. We make no guarantees about specific outcomes. Our total liability is limited to the purchase price of the product." },
      { h: "Governing law", p: "These terms are governed by the laws of Brazil. Any disputes shall be resolved in the courts of São Paulo, SP, Brazil." }
    ]
  },
  refunds: {
    title: "Refund Policy",
    updated: "12 May 2026",
    sections: [
      { h: "Our guarantee", p: "We want you to be completely satisfied with your purchase. If our ebook doesn't meet your expectations, we offer a straightforward refund process." },
      { h: "Refund window", p: "You may request a full refund within 7 days of purchase, no questions asked. Simply email emaildoalvaroabreu@gmail.com with your order confirmation." },
      { h: "How to request a refund", p: "Send an email to emaildoalvaroabreu@gmail.com with the subject line \"Refund request\" and include your purchase email and the title of the ebook. We will process your refund within 5 business days." },
      { h: "Refund method", p: "Refunds are issued to the original payment method. Processing times depend on your bank or card issuer, typically 5–10 business days after we confirm the refund." },
      { h: "After the refund window", p: "After 7 days, refund requests are reviewed on a case-by-case basis. We aim to be fair and reasonable in all circumstances." },
      { h: "Chargebacks", p: "We kindly ask that you contact us before initiating a chargeback with your bank. We are committed to resolving issues quickly and directly." }
    ]
  }
};

const StudioLegal = ({ page, onHome, t, isMobile, isTablet }) => {
  var content = LEGAL_CONTENT[page];
  if (!content) return null;
  var pad = isMobile ? "32px 16px 64px" : isTablet ? "48px 32px 80px" : "64px 0 96px";
  return (
    <section style={{ maxWidth: 720, margin: "0 auto", padding: pad, fontFamily: '"Inter", system-ui, sans-serif' }}>
      <button onClick={onHome} style={{ background: "none", border: "none", cursor: "pointer", fontFamily: '"Inter", system-ui, sans-serif', fontSize: 14, color: "var(--stuMute)", marginBottom: 24, padding: 0 }}>
        ← Back to catalog
      </button>
      <h1 style={{ fontFamily: '"Fraunces", serif', fontSize: isMobile ? 32 : 40, fontWeight: 400, color: "var(--stuInk)", letterSpacing: "-0.02em", marginBottom: 8 }}>
        {content.title}
      </h1>
      <p style={{ fontSize: 13, color: "var(--stuMute)", marginBottom: 40 }}>Last updated: {content.updated}</p>
      {content.sections.map((s, i) => (
        <div key={i} style={{ marginBottom: 32 }}>
          <h2 style={{ fontFamily: '"Inter", system-ui, sans-serif', fontSize: 18, fontWeight: 600, color: "var(--stuInk)", marginBottom: 8 }}>{s.h}</h2>
          <p style={{ fontSize: 15, lineHeight: 1.7, color: "var(--stuInk)", opacity: 0.85, margin: 0 }}>{s.p}</p>
        </div>
      ))}
    </section>
  );
};

/* ─── About view ────────────────────────────────────────────── */

const StudioAbout = ({ onHome, t, isMobile, isTablet }) => (
  <React.Fragment>
    <section style={{...stuStyles.aboutHero, ...(isMobile ? { padding: "48px 16px 32px" } : isTablet ? { padding: "72px 32px 48px" } : {})}}>
      <div style={stuStyles.kicker}>{t("aboutHeroTitle1")}</div>
      <h1 style={{...stuStyles.aboutHeroTitle, ...(isMobile ? { fontSize: 36, letterSpacing: -1 } : isTablet ? { fontSize: 52, letterSpacing: -1.4 } : {})}}>
        I'm Alvaro — and this is a one-person<br/>
        <span style={stuStyles.heroAccent}>operation</span>, by design.
      </h1>
      <p style={stuStyles.aboutHeroLede}>
        I write short, practical books for people who'd rather adopt a tool well
        than chase the next one. The catalog is small on purpose, runs in three
        languages, and grows by one or two titles a week.
      </p>
    </section>

    <section style={{...stuStyles.aboutGrid, ...(isMobile ? { gridTemplateColumns: "1fr", padding: "32px 16px", gap: 32 } : isTablet ? { gridTemplateColumns: "1fr", padding: "48px 32px", gap: 40 } : {})}}>
      <div style={stuStyles.aboutTextCol}>
        <h3 style={stuStyles.aboutH3}>{t("whyExist")}</h3>
        <p style={stuStyles.aboutP}>
          Most "AI" content today is noise: hype-cycle posts, recycled prompts,
          courses that promise the world and deliver a Notion template. I wanted
          to read something different — short, honest, useful this year — and
          when I couldn't find enough of it, I started writing it.
        </p>
        <p style={stuStyles.aboutP}>
          Each title is built around a single reader: a solo lawyer, a parent
          waiting for the NHS, a small business in Spain. The promise is small
          and concrete: read it once, keep it on hand, get measurable use out of
          it within a week.
        </p>

        <h3 style={stuStyles.aboutH3}>{t("howBuilt")}</h3>
        <p style={stuStyles.aboutP}>
          I don't write everything from scratch in a vacuum. I research,
          interview, prototype workflows on real businesses, and then condense
          what worked. AI sits in the writing room — it speeds up research and
          drafting — but the editorial point of view, the structure, and the
          last pass are human.
        </p>
        <p style={stuStyles.aboutP}>
          Distribution is intentionally simple: Kiwify for most titles, with a
          handful on Stripe and Gumroad. No bundles, no urgency timers, no
          "limited slots." If a book is up, it's up. If a price changes, it
          changes once and stays.
        </p>

        <h3 style={stuStyles.aboutH3}>{t("whatWontDo")}</h3>
        <ul style={stuStyles.aboutList}>
          <li>Write to a trend I don't actually use.</li>
          <li>Charge $497 for what's worth $19.</li>
          <li>Ship a book I wouldn't read myself.</li>
          <li>Email you more than once a month.</li>
        </ul>
      </div>

      <aside style={{...stuStyles.aboutSide, ...((isMobile || isTablet) ? { position: "static" } : {})}}>
        <div style={stuStyles.aboutSideCard}>
          <div style={stuStyles.kicker}>{t("studioNumbers")}</div>
          <div style={stuStyles.aboutFacts}>
            <div style={stuStyles.aboutFact}><div style={stuStyles.aboutFactN}>1</div><div style={stuStyles.aboutFactL}>{t("person")}</div></div>
            <div style={stuStyles.aboutFact}><div style={stuStyles.aboutFactN}>{products.length}</div><div style={stuStyles.aboutFactL}>{t("titles")}</div></div>
            <div style={stuStyles.aboutFact}><div style={stuStyles.aboutFactN}>3</div><div style={stuStyles.aboutFactL}>{t("languages").toLowerCase()}</div></div>
            <div style={stuStyles.aboutFact}><div style={stuStyles.aboutFactN}>6</div><div style={stuStyles.aboutFactL}>{t("markets").toLowerCase()}</div></div>
          </div>
        </div>

        <div style={stuStyles.aboutSideMeta}>
          <div style={stuStyles.aboutSideRow}><span style={stuStyles.statLbl}>{t("based")}</span><span style={stuStyles.statVal}>São Paulo, BR</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.aboutSideRow}><span style={stuStyles.statLbl}>{t("writingSince")}</span><span style={stuStyles.statVal}>2026</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.aboutSideRow}><span style={stuStyles.statLbl}>{t("distribution")}</span><span style={stuStyles.statVal}>Kiwify · Stripe · Gumroad</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.aboutSideRow}><span style={stuStyles.statLbl}>{t("schedule")}</span><span style={stuStyles.statVal}>1–2 titles / week</span></div>
        </div>
      </aside>
    </section>

    <section style={{...stuStyles.aboutCloser, ...(isMobile ? { padding: "40px 16px 60px" } : isTablet ? { padding: "48px 32px 80px" } : {})}}>
      <h2 style={{...stuStyles.aboutCloserTitle, ...(isMobile ? { fontSize: 32 } : isTablet ? { fontSize: 40 } : {})}}>{t("closerTitle1")}<br/>{t("closerTitle2")}</h2>
      <a style={stuStyles.btnPrimary} onClick={onHome}>{t("browseCatalog")}</a>
    </section>
  </React.Fragment>
);

/* ─── Notes view ────────────────────────────────────────────── */

const NOTES = [
  {
    id: "n01", date: "May 8, 2026", readTime: "4 min", category: "Process",
    title: "Why I write short books instead of long courses",
    excerpt: "A 90-page book that actually gets finished beats a 12-hour course that sits in a tab. The math on attention is brutal — and it's why the catalog is built the way it is.",
    metaDescription: "Why short, practical ebooks outperform long online courses for independent publishers. The attention math, completion rates, and business model behind a growing catalog.",
    sections: [
      { heading: "The completion problem nobody talks about", body: "The average online course has a completion rate somewhere between 5% and 15%. That is not a typo. For every hundred people who buy a twelve-hour video course, fewer than fifteen will ever reach the end. Most will watch the first two modules, feel productive for an afternoon, and never come back.\n\nI know this because I was one of those people. My browser had seventeen bookmarked courses I had paid for and never finished. The intention was real. The follow-through was not. And yet the creators of those courses kept selling them, because the business model does not depend on completion — it depends on the promise of transformation.\n\nThat felt wrong to me. Not morally wrong, necessarily, but structurally misaligned. I wanted to build something where the reader finishing the book was not the exception — it was the baseline expectation." },
      { heading: "Why ninety pages changes the equation", body: "A 90-page book takes most people between two and four hours to read. That is one flight. One quiet Sunday morning. One week of twenty-minute lunch breaks. The finish line is visible from the start, and that changes everything about how people engage with the material.\n\nWhen you can see the end, you commit differently. You read with a pen. You try things as you go. You finish, and because you finished, you actually get the value you paid for. The book becomes a tool you keep on hand — not a tab you close.\n\nThis is also why I keep the scope tight. Each title answers one question for one reader. Not \"everything you need to know about AI\" — but \"27 workflows you can copy-paste into your Tuesday.\" The constraint is the feature." },
      { heading: "The business math works differently too", body: "A course at $197 needs expensive marketing, long sales pages, webinar funnels, and urgency timers. It optimizes for one large transaction per customer. The economics push you toward hype, because the price demands a proportionally large promise.\n\nA book at $17 needs none of that. The price is low enough that curiosity is sufficient motivation. No cart abandonment drama. No countdown clocks. The reader buys it, reads it, and if it delivers, they come back for the next one. You build a catalog, not a launch cycle.\n\nAt 56 titles across six markets, this is compounding. Each new book adds a permanent revenue line. There is no launch week spike followed by a decline to zero. The catalog earns while I sleep — modest amounts per title, but the aggregate grows every month." },
      { heading: "Short does not mean shallow", body: "The most common objection I hear is that short books lack depth. This misunderstands what depth means in a practical context. Depth is not word count — it is the ratio of actionable insight to total text.\n\nA 400-page business book often has thirty pages of real insight surrounded by 370 pages of anecdotes, case studies, and repetition designed to justify the hardcover price. I am not criticizing those books — some of my favorites are long. But the format serves the publisher, not necessarily the reader.\n\nMy books are dense. Every chapter has a reason to exist. Every page earns its place. The editing process removes more than it adds. The goal is a book you can read once, keep on your desk, and reference whenever the situation calls for it." },
      { heading: "What this means for the catalog going forward", body: "I will keep writing short books. One or two a week, across English, Spanish, and Portuguese. Each one targeting a specific reader with a specific problem. The format is the strategy — it is not a limitation I am working around.\n\nIf you have been sitting on a twelve-hour course you never finished, consider trying a book that respects your Tuesday. That is the pitch. No urgency, no scarcity, no countdown. The book will be here when you are ready." }
    ]
  },
  {
    id: "n02", date: "Apr 24, 2026", readTime: "6 min", category: "AI",
    title: "On using AI without becoming a hype machine",
    excerpt: "AI is a writing-room tool, not a religion. Here's how I draft, where I stop, and why every book gets a human last pass before it ships.",
    metaDescription: "How an independent publisher uses AI tools like Claude and ChatGPT for research and drafting without sacrificing editorial integrity. A practical framework for honest AI-assisted writing.",
    sections: [
      { heading: "The disclosure I wish more creators would make", body: "I use AI in my writing process. Claude for long-form drafting and structural editing. ChatGPT for quick research and brainstorming. Perplexity for sourced research when I need citations I can verify.\n\nI am not embarrassed about this, and I am not hiding it. But I also refuse to pretend that AI writes my books. It does not. AI is a tool in the writing room — the same way a calculator is a tool in accounting. The accountant still decides what to calculate and whether the result makes sense.\n\nWhat bothers me about the current AI discourse is the binary framing. Either you are \"AI-powered\" (implying the machine does the real work) or you are \"human-only\" (implying some kind of purity). Neither frame is honest. The interesting question is not whether you use AI, but how, and where you draw the line." },
      { heading: "Where AI sits in my workflow", body: "Here is my actual process, stripped of any pretense:\n\nResearch phase: I use Perplexity and ChatGPT to survey a topic quickly. What are the current regulations? What changed in the last six months? What are practitioners actually struggling with? This replaces hours of Googling, not hours of thinking.\n\nOutline phase: I write the outline myself. The structure of a book — what goes first, what builds on what, where to place the practical exercises — is editorial judgment. AI is bad at this because it optimizes for completeness, not for the reader's journey through the material.\n\nDrafting phase: I use Claude for first drafts of individual sections. I give it my outline, my voice notes, my rough points, and ask it to draft a section. The output is maybe 60% usable. The other 40% is generic, repetitive, or misses the tone I want.\n\nEditing phase: This is entirely human. I rewrite, cut, reorganize, and read aloud. Every book gets at least two full editing passes where I am not adding — I am subtracting. If a sentence sounds like it could appear in any book on the topic, it gets cut." },
      { heading: "The line I will not cross", body: "I will not publish a book I have not read, edited, and approved line by line. I will not let AI write the opinion sections — the parts where I say \"this works\" or \"this is overrated\" or \"skip this unless you are in situation X.\" Those are editorial positions, and they need to come from someone who has actually tested the material.\n\nI also will not use AI to inflate word count. If a book is 80 pages and the material is covered, it ships at 80 pages. AI makes it trivially easy to pad content with transitions, summaries, and restated points. Readers can feel that padding, even if they cannot articulate why the book feels watery." },
      { heading: "Why this matters for trust", body: "Every book in this catalog carries my name. Not a brand name, not a pseudonym — my actual name. If a reader buys the AI Workflow Playbook and finds that it is generic ChatGPT output reformatted as a PDF, they will not buy the next book. They will not trust the catalog. They will not recommend it to anyone.\n\nTrust is the only asset a solo publisher has. I cannot outspend the marketing budgets of course empires. I cannot out-volume the content farms. What I can do is deliver consistent, honest, useful work — and AI helps me do that faster without compromising the standard.\n\nThe day I cannot tell the difference between my work and raw AI output is the day I stop publishing. That day has not come, and I work actively to keep it distant." },
      { heading: "A practical framework for other creators", body: "If you are a creator wondering how to use AI honestly, here is what I would suggest: use it for research, first drafts, and idea generation. Do not use it for opinions, editorial decisions, or the final voice of the piece. Always do a human last pass — reading the full text aloud is the simplest test. If it sounds like a machine wrote it, rewrite until it does not.\n\nAnd disclose it. Not with a giant disclaimer banner, but matter-of-factly, the way you would mention any other tool in your process. Your readers are smart enough to appreciate honesty, and suspicious enough to punish evasion." }
    ]
  },
  {
    id: "n03", date: "Apr 11, 2026", readTime: "3 min", category: "Operating",
    title: "One person, three languages, six markets",
    excerpt: "How the catalog is structured behind the scenes — naming, pricing, distribution, and the boring spreadsheet that makes the whole thing work.",
    metaDescription: "Behind-the-scenes look at running a multilingual ebook catalog across six markets as a solo publisher. Naming conventions, pricing strategy, and distribution logistics.",
    sections: [
      { heading: "The spreadsheet that runs the operation", body: "At 56 titles across English, Spanish, and Portuguese, the catalog needs a system. Not a complicated one — just a boring, reliable spreadsheet that tracks every title, its market, its price in local currency, the Kiwify product URL, the publication date, and the current status.\n\nEvery Sunday evening I update this spreadsheet. It takes fifteen minutes. It is the single most important operational habit in the business, because without it, I would lose track of what is live, what needs updating, and what is earning.\n\nThe system is not glamorous. It is a Google Sheet with six tabs — one per market. Each row is a product. The columns are: ID, title, slug, market, category, price (local), price (USD equivalent), Kiwify URL, publication date, last update, status. That is it." },
      { heading: "How pricing works across currencies", body: "Pricing a $19 book in pounds, euros, and reais is not a simple currency conversion. Each market has its own price sensitivity, its own competitive landscape, and its own expectations about what a digital book should cost.\n\nFor the UK market, I price in round pounds — £9, £12, £14. Not £8.97 or £11.99. The rounding signals confidence, not discount-bin energy. For Spain, I use .97 endings because that is what the market expects for digital products. For Australia, I match USD pricing because the audiences overlap significantly.\n\nBrazil is different. The one Brazilian product (Stack USD) is a high-ticket course at R$1,997 — a completely different positioning than the low-ticket ebook catalog. It targets a specific audience of Brazilian infoproduct creators who want to sell in dollars." },
      { heading: "Distribution is intentionally simple", body: "Kiwify handles most of the catalog. Checkout, delivery, tax handling — one platform for the majority of sales. I considered splitting across Gumroad, Stripe, and Kiwify, but the operational overhead was not worth it for a one-person shop.\n\nThe rule is simple: unless there is a specific reason to use another platform, it goes on Kiwify. The exception is the high-ticket Brazilian product, which uses a dedicated sales page. Everything else follows the same pattern: product page on the studio site, buy button links to Kiwify, delivery is instant.\n\nThis simplicity is a feature. I spend zero hours a week on distribution logistics, which means more hours writing." },
      { heading: "Why three languages instead of one", body: "The obvious question is: why not just write in English? English is the largest market. It would simplify everything.\n\nThe answer is that I speak three languages natively, and each language opens a market that is underserved by quality independent publishers. Spanish-language ebooks on practical topics like exam preparation, digital nomad visas, or small business AI — the competition is thin. Portuguese-language content for Brazilian creators — even thinner.\n\nEach language is a moat. The effort to translate and localize is real, but it gives me access to markets where English-only publishers cannot compete. A book about UK renters' rights with pets does not need a Spanish version. But a book about anxiety management techniques works across all three languages with cultural adaptation.\n\nThe key word is adaptation, not translation. Each version is written for its audience, not machine-translated from English." }
    ]
  },
  {
    id: "n04", date: "Mar 28, 2026", readTime: "5 min", category: "Pricing",
    title: "Why $7 is sometimes the right price",
    excerpt: "Most independent publishers price by ego. I price by what the book is worth as a tool to the reader. Some are $7. A few are $39. None are $497.",
    metaDescription: "Ebook pricing strategy for independent publishers: why low-ticket pricing at $7-$39 builds a sustainable catalog business. The math behind pricing digital products for volume.",
    sections: [
      { heading: "The pricing trap most indie publishers fall into", body: "There is a prevailing belief in the digital product world that higher prices signal higher value. Price it at $497 and it must be premium. Price it at $7 and it must be throwaway. This logic sounds reasonable and is almost entirely wrong.\n\nThe price of a digital product should reflect the value it delivers relative to the reader's context. A book that saves a UK sole trader £200 on their tax return is worth £17 — not because of its page count, but because of the return on investment. A book that helps someone manage anxiety with daily exercises is worth £9 — the price of two coffees for twenty-one days of guided practice.\n\nI do not price by production cost (which is near zero for digital). I do not price by how much time I spent writing (which is irrelevant to the reader). I price by asking: what would a reasonable person pay for this specific outcome, knowing they can get a refund in seven days if it does not deliver?" },
      { heading: "The math of low-ticket at scale", body: "A $17 book that sells 10 copies a day generates $5,100 a month. At 56 titles, if each sells an average of 2 copies a day, the catalog generates roughly $57,000 a month. Not every title performs equally — some sell 8 copies a day, some sell one every few days. But the portfolio effect smooths the variance.\n\nCompare this to the course model: a $497 course needs a launch, a webinar funnel, paid ads, and urgency mechanics to generate the same revenue. One course at $497 needs 114 sales per month for $57K. The marketing cost to generate those sales often consumes 40-60% of revenue.\n\nMy marketing cost is close to zero for most titles. The catalog is the marketing. Each book is a gateway to the next one. Each satisfied reader is a potential buyer of three or four more titles. The flywheel is slow but durable." },
      { heading: "When I price higher — and why", body: "Not everything in the catalog is $7. The Agent Compliance Roadmap is $39 because it targets CTOs and founders with meaningful budgets, and the content replaces a $5,000 compliance consultation. The Cursor 3 Power-User Pack is $29 because it includes 80 configuration files that save developers hours of setup time.\n\nThe pricing logic is consistent: what is the outcome worth to this specific reader? For a developer billing at $150/hour, saving ten hours of configuration is worth $1,500. Charging $29 for that is not low pricing — it is aggressive value pricing that makes the purchase feel like a no-brainer.\n\nThe one product priced in the thousands — Stack USD at R$1,997 — follows different rules entirely. It is a comprehensive course for a narrow professional audience, not a quick-read handbook. Different product, different price, different logic." },
      { heading: "What I will never do with pricing", body: "I will never use urgency timers. No \"only 3 seats left\" on a digital product with infinite inventory. No \"price doubles at midnight\" manufactured scarcity. These tactics work in the short term and erode trust in the long term.\n\nI will never bundle fifteen books at 90% off to inflate perceived value. If a book is worth $17, it is worth $17. If you want three books, you pay for three books. The pricing is honest, which means it needs no explanation or justification.\n\nI will never raise prices retroactively on existing customers. If you bought the book at $14 and I later price it at $19, you paid $14. Updates for twelve months are included regardless of what the current price is.\n\nPricing is a trust signal. Every decision I make about it should pass a simple test: would I be comfortable if the reader saw exactly how and why I set this price? If yes, the price is right." },
      { heading: "The bottom line on pricing strategy", body: "Price for the reader, not for your ego. Price for volume, not for one-time jackpots. Price honestly, and let the catalog compound. A $7 book that reaches the right reader and delivers real value is worth more to your business than a $497 course that sits in a tab and generates refund requests.\n\nThe market rewards patience and consistency. It punishes hype and manufactured urgency. I have chosen my side of that equation." }
    ]
  },
  {
    id: "n05", date: "Mar 14, 2026", readTime: "4 min", category: "Process",
    title: "Editorial discipline beats output volume",
    excerpt: "I'd rather publish one tight book a week than four mediocre ones. Here's the editing checklist every title goes through before it goes live.",
    metaDescription: "The editing checklist and quality control process behind publishing one ebook per week. Why editorial discipline matters more than output volume for independent publishers.",
    sections: [
      { heading: "The temptation of speed", body: "With AI tools, I could publish four or five books a week. The research is faster, the drafting is faster, the formatting is faster. Everything in the pipeline has accelerated — except the one step that matters most: the editorial pass.\n\nThe editorial pass is where a draft becomes a book. It is where generic sentences get cut, where the structure gets tested against a real reader's journey, and where the author's actual point of view emerges from the noise of assembled information. This step cannot be rushed, cannot be automated, and cannot be skipped.\n\nI publish one or two titles a week. Not because I cannot produce more, but because I cannot edit more without dropping the standard." },
      { heading: "The seven-point checklist", body: "Every title goes through this checklist before it ships. No exceptions.\n\nOne: Does the book answer one specific question for one specific reader? If the scope has crept during writing, I cut until the focus is restored.\n\nTwo: Can the reader finish it in a single sitting? If the book takes more than four hours to read, something needs to go.\n\nThree: Does every chapter earn its place? I read the table of contents as if it were a pitch. If a chapter title does not make the reader want to keep going, it gets rewritten or merged.\n\nFour: Have I removed every sentence that sounds like it could appear in any book on this topic? Generic advice is the enemy. If it reads like a ChatGPT summary, it gets cut.\n\nFive: Are the practical elements — templates, checklists, workflows — immediately usable without additional research? The reader should be able to act on chapter three today, not \"after they figure out the prerequisites.\"\n\nSix: Have I read the entire book aloud? This catches rhythm problems, run-on sentences, and tonal inconsistencies that silent reading misses.\n\nSeven: Would I recommend this book to a friend who asked me about the topic? This is the final gate. If the answer is not an immediate yes, it does not ship." },
      { heading: "What gets cut and why", body: "In my most recent editing round, I cut roughly 30% of the drafted content. The cuts fell into predictable categories:\n\nContext dumps — long paragraphs explaining why the topic matters. If the reader bought the book, they already know why it matters. Get to the useful part.\n\nHedged advice — sentences like \"you might want to consider potentially exploring the option of...\" Either recommend it or do not. Readers pay for decisiveness, not diplomatic ambiguity.\n\nRedundant examples — one good example is worth more than three mediocre ones. I keep the example that makes the concept click and remove the rest.\n\nTransitional padding — \"Now that we have covered X, let us turn our attention to Y.\" The reader can see the next chapter heading. They do not need a guide." },
      { heading: "The cost of discipline and why it is worth paying", body: "Publishing fewer books means slower revenue growth in the short term. Every title I do not publish is potential income left on the table. I am aware of this trade-off and I accept it.\n\nThe alternative — publishing four mediocre books a week — would generate more revenue for approximately three months. Then the reviews would start reflecting the quality drop. Then the repeat purchase rate would decline. Then the catalog would become a liability rather than an asset.\n\nOne good book a week, fifty-two weeks a year, is fifty-two permanent additions to a catalog that earns while I sleep. In three years, that is over 150 titles, each one maintaining the standard that builds trust and repeat purchases. The math favors discipline. It always does." }
    ]
  },
];

const StudioNotes = ({ notes, onNote, t, isMobile, isTablet }) => (
  <React.Fragment>
    <section style={{...stuStyles.indexHero, ...(isMobile ? { padding: "48px 16px 24px" } : isTablet ? { padding: "72px 32px 32px" } : {})}}>
      <div style={stuStyles.kicker}>{t("notes")}</div>
      <h1 style={{...stuStyles.indexHeroTitle, ...(isMobile ? { fontSize: 32, letterSpacing: -0.8 } : isTablet ? { fontSize: 48, letterSpacing: -1.2 } : {})}}>{t("notesHeroTitle1")}<br/><span style={stuStyles.heroAccent}>{t("notesHeroTitle2")}</span></h1>
      <p style={stuStyles.indexHeroLede}>{t("notesHeroLede")}</p>
    </section>

    <section style={{...stuStyles.indexList, ...(isMobile ? { padding: "24px 16px 60px" } : isTablet ? { padding: "32px 32px 80px" } : {})}}>
      {(notes || NOTES).map((n, i) => (
        <article key={n.id} style={{...stuStyles.indexItem, ...(isMobile ? { gridTemplateColumns: "1fr", gap: 8 } : {})}} onClick={() => onNote(n.id)}>
          {!isMobile && <div style={stuStyles.indexNum}>{String(i + 1).padStart(2, "0")}</div>}
          <div style={stuStyles.indexBody}>
            <div style={stuStyles.indexMeta}>
              <span>{n.date}</span><span style={stuStyles.indexDot}>·</span>
              <span>{n.category}</span><span style={stuStyles.indexDot}>·</span>
              <span>{n.readTime} {t("readSuffix")}</span>
            </div>
            <h2 style={stuStyles.indexTitle}>{n.title}</h2>
            <p style={stuStyles.indexExcerpt}>{n.excerpt}</p>
            <a style={stuStyles.indexLink}>{t("readTheNote")}</a>
          </div>
        </article>
      ))}
    </section>
  </React.Fragment>
);

const StudioNoteDetail = ({ note, notes, onBack, onNote, t, isMobile, isTablet }) => {
  const n = note;
  const arr = notes || NOTES;
  const currentIdx = arr.findIndex(x => x.id === n.id);
  const nextNote = arr[currentIdx + 1];
  const prevNote = currentIdx > 0 ? arr[currentIdx - 1] : null;

  return (
    <React.Fragment>
      <div style={{...stuStyles.crumb, ...(isMobile ? { padding: "20px 16px 0", flexWrap: "wrap" } : isTablet ? { padding: "24px 32px 0" } : {})}}>
        <a style={stuStyles.crumbLink} onClick={onBack}>← {t("notes")}</a>
        <span style={stuStyles.crumbSep}>/</span>
        <span style={stuStyles.crumbCat}>{n.category}</span>
      </div>

      <article style={{...stuStyles.articleWrap, ...(isMobile ? { padding: "24px 16px 60px" } : isTablet ? { padding: "32px 32px 80px" } : {})}}>
        <header style={stuStyles.articleHeader}>
          <div style={stuStyles.kicker}>{n.category}</div>
          <h1 style={{...stuStyles.articleH1, ...(isMobile ? { fontSize: 30, letterSpacing: -0.6 } : isTablet ? { fontSize: 40, letterSpacing: -1 } : {})}}>{n.title}</h1>
          <div style={stuStyles.articleMeta}>
            <span>{n.date}</span>
            <span style={stuStyles.indexDot}>·</span>
            <span>{n.readTime} {t("readSuffix")}</span>
            <span style={stuStyles.indexDot}>·</span>
            <span>{t("byAuthor")}</span>
          </div>
          <p style={stuStyles.articleLede}>{n.excerpt}</p>
        </header>

        <div style={stuStyles.articleBody}>
          {n.sections.map((s, i) => (
            <section key={i}>
              <h2 style={stuStyles.articleH2}>{s.heading}</h2>
              {s.body.split("\n\n").map((p, j) => (
                <p key={j} style={stuStyles.articleP}>{p}</p>
              ))}
            </section>
          ))}
        </div>

        <nav style={stuStyles.articleNav}>
          {prevNote && (
            <a style={stuStyles.articleNavLink} onClick={() => onNote(prevNote.id)}>
              <span style={stuStyles.articleNavDir}>{t("previous")}</span>
              <span style={stuStyles.articleNavTitle}>{prevNote.title}</span>
            </a>
          )}
          {!prevNote && <div />}
          {nextNote && (
            <a style={{...stuStyles.articleNavLink, textAlign: "right"}} onClick={() => onNote(nextNote.id)}>
              <span style={stuStyles.articleNavDir}>{t("next")}</span>
              <span style={stuStyles.articleNavTitle}>{nextNote.title}</span>
            </a>
          )}
        </nav>
      </article>
    </React.Fragment>
  );
};

/* ─── Reviews view ──────────────────────────────────────────── */

const REVIEWS = [
  {
    id: "r05", date: "Mar 13, 2026", category: "Books", rating: 5.0, affiliate: null, cta: "See the list", affiliateUrl: null,
    title: "Five books on solo operating that aged well",
    excerpt: "Not a listicle. Five titles I keep going back to because they get the math of one-person businesses right, and don't pretend the work is glamorous.",
    metaDescription: "Five essential books for solopreneurs and one-person businesses that have stood the test of time. Practical recommendations from an indie publisher running a 56-title catalog.",
    verdict: "These five books shaped how I think about running a one-person operation. None of them promise glamour. All of them deliver substance.",
    sections: [
      { heading: "Why these five and not fifty", body: "The internet is full of book recommendation lists. Most of them are padded with titles the author has not actually read, optimized for affiliate commissions rather than genuine utility. I wanted to write something different.\n\nThese are five books I have read multiple times, referenced in my own work, and recommended to friends who were starting solo businesses. They share one quality: they treat the reader as an adult who wants practical frameworks, not motivational platitudes.\n\nI earn nothing from recommending these. No affiliate links. No partnership deals. If you buy any of them, I see zero revenue. That should tell you something about how strongly I believe they are worth your time." },
      { heading: "Company of One by Paul Jarvis", body: "This is the book that reframed how I think about growth. Jarvis argues that the default assumption — bigger is better, growth is mandatory, scaling is the goal — is not a law of nature. It is a choice. And for many businesses, choosing to stay small is the more profitable, more sustainable, more enjoyable path.\n\nThe book is not anti-growth. It is anti-unexamined-growth. Every chapter asks: do you actually need to scale this, or are you scaling because that is what business advice tells you to do? For a solo publisher with 56 titles, this question is relevant every single day.\n\nI revisit the chapter on determining the \"enough\" threshold at least once a quarter." },
      { heading: "The E-Myth Revisited by Michael Gerber", body: "Gerber's central insight — that most small businesses fail because the founder works in the business instead of on the business — is thirty years old and still the most important piece of operational advice I know.\n\nThe book is slightly dated in its examples, but the framework is timeless. Work on your business as if you were going to franchise it. Document every process. Build systems that do not depend on you being present. This is exactly how I run the catalog: every title follows the same production pipeline, the same quality checklist, the same distribution process.\n\nIf the process is documented, it can be delegated. If it can be delegated, you are free to do the work that only you can do." },
      { heading: "Show Your Work by Austin Kleon", body: "Kleon makes the case that sharing your process — not just your output — is the most effective form of marketing for creative work. This book directly influenced the Notes section of this website. Every essay I publish about my writing process, my pricing decisions, my editorial standards — that is Show Your Work in practice.\n\nThe book is short, visual, and unusually humble for a book about self-promotion. Kleon is not teaching you to be a personal brand. He is teaching you to be generous with what you know and let the audience find you.\n\nI read it in one sitting and started writing the next day." },
      { heading: "Anything You Want by Derek Sivers", body: "This is the shortest and most opinionated book on the list. Sivers built CD Baby into a $100 million business and sold it, and the book is forty vignettes about what he learned. The lesson that stuck with me: make every decision based on whether it makes you say \"hell yes\" or not. If it is not a \"hell yes,\" it is a no.\n\nThis philosophy drives my catalog decisions. I do not write books on topics I am indifferent about, even if the market data says they would sell. Every title in the catalog exists because I genuinely wanted to write it. The financial return is a consequence of that enthusiasm, not a substitute for it." },
      { heading: "Rework by Jason Fried and David Heinemeier Hansson", body: "Rework is the operational companion to Company of One. Where Jarvis asks \"do you need to grow?\", Fried and Hansson ask \"do you need to do this at all?\" The book is a series of short essays, each one challenging a conventional business assumption.\n\nMeetings are toxic. Planning is guessing. Inspiration is perishable. Fire the workaholics. These are not provocations for their own sake — they are distilled lessons from building Basecamp as a profitable, calm company in a world of venture-funded chaos.\n\nI re-read Rework whenever I catch myself overcomplicating the operation. It is the best reset button I know." }
    ]
  },
  // ── External reviews (reviews.alvaroabreu.com) ──
  { id: "burnout-escape-plan-before-you-buy", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "The Burnout Escape Plan: Is It Worth £8.99? An Honest Chapter-by-Chapter Review", excerpt: "What's inside, who it helps, who needs actual therapy instead, what's genuinely good, and what's missing. The buyer's guide we'd want if we hadn't written it.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-before-you-buy/" },
  { id: "burnout-escape-plan-beginners", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Think You Might Be Burnt Out? Start Here.", excerpt: "A gentle guide to recognising burnout symptoms, understanding what's happening, and taking your first small step toward feeling better. No jargon, no judgment, no pressure.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-beginners/" },
  { id: "burnout-escape-plan-checklist", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Your burnout recovery checklist: 15 steps in the right order", excerpt: "Recovery from burnout isn't about doing everything at once. It's about doing the right things in the right sequence. Here's the order that actually works, based on CBT principle...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-checklist/" },
  { id: "burnout-escape-plan-complete-guide", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "The Complete Guide to Burnout Recovery for UK Professionals", excerpt: "Everything you need to understand, diagnose, and recover from burnout — including what the NHS offers, what it doesn't, and how to bridge the gap with evidence-based self-help.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-complete-guide/" },
  { id: "burnout-escape-plan-cost-of-burnout", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "The real cost of burnout — to your career, health, and wallet", excerpt: "Burnout isn't just feeling tired. It's an accumulating debt — against your health, your career trajectory, your relationships, and your finances. Here's what it's actually costi...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-cost-of-burnout/" },
  { id: "burnout-escape-plan-editorial", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Britain's Young Professionals Are Burning Out — and the System Isn't Built to Catch Them", excerpt: "An editorial on the structural failure behind the UK's burnout epidemic. When 96% of a generation reports extreme stress and the NHS can't respond for 53 days, what fills the ga...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-editorial/" },
  { id: "burnout-escape-plan-expert", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "What CBT Therapists Actually Recommend for Burnout Recovery", excerpt: "If you could get a CBT therapy appointment tomorrow — without the 18-week wait, without the £80 per session cost — what would the therapist actually tell you? This article cover...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-expert/" },
  { id: "burnout-escape-plan-faq", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Am I Burnt Out or Just Tired? 15 Questions Every UK Professional Is Asking", excerpt: "The honest answers — clinical and practical — to the questions you're typing into Google at 2am. Including when a self-help guide is enough, and when it isn't.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-faq/" },
  { id: "burnout-escape-plan-free-vs-paid", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "NHS Resources vs a Paid Guide — Which Actually Helps with Burnout?", excerpt: "You should not have to pay for mental health support. In an ideal world, the NHS would provide fast, accessible burnout recovery for everyone. We do not live in that world. This...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-free-vs-paid/" },
  { id: "burnout-escape-plan-how-to", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "How to Recover From Burnout in the UK — A Practical 5-Step Plan for 2026", excerpt: "The NHS can't see you for 53 days. Private therapy costs £600. Here's what to do in the meantime — and how a £8.99 guide bridges the gap with real CBT techniques.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-how-to/" },
  { id: "burnout-escape-plan-mistakes", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "8 Recovery Mistakes That Keep You Burnt Out Longer", excerpt: "You tried a long weekend. You tried a meditation app. You might have even taken a week off. And then you came back and within forty-eight hours you felt exactly the same. That i...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-mistakes/" },
  { id: "burnout-escape-plan-myths", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "9 Myths About Burnout That Keep You Stuck", excerpt: "The beliefs that prevent recovery — debunked with research, NHS data, and the lived experience of UK professionals who've been where you are.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-myths/" },
  { id: "burnout-escape-plan-results", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Burnout Recovery Results: 4 UK Professionals Share What Actually Changed", excerpt: "A marketing manager who couldn't sleep, a software engineer drowning in Slack, a junior doctor running on fumes, and a teacher one term away from quitting. Four burnout stories....", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-results/" },
  { id: "burnout-escape-plan-review", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "I Was Working 60-Hour Weeks and Calling It Hustle. This £8.99 Guide Changed Everything.", excerpt: "A personal review of The Burnout Escape Plan — the CBT-based recovery guide for British professionals who can't get a therapy appointment before they break.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-review/" },
  { id: "burnout-escape-plan-scripts", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Boundary Scripts for Burnt-Out Professionals — Copy and Use", excerpt: "You know you need to set boundaries. You have read articles that tell you to 'learn to say no.' The problem is not understanding the concept — it is finding the actual words. Wh...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-scripts/" },
  { id: "burnout-escape-plan-timeline", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "How Long Does Burnout Recovery Actually Take?", excerpt: "You searched this because you want a number. A week? A month? Three months? The honest answer is that it depends — but not in the vague, unhelpful way that phrase is usually use...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-timeline/" },
  { id: "burnout-escape-plan-vs-alternatives", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Best Burnout Recovery Options for UK Professionals in 2026 — 5 Approaches Compared", excerpt: "NHS therapy, private CBT, meditation apps, self-help books, and a £8.99 guide that tries to bridge the gap. Here's how they stack up when you're burnt out and need help now.", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-vs-alternatives/" },
  { id: "burnout-escape-plan-who", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Am I Actually Burnt Out? 12 Signs It's More Than Tiredness", excerpt: "You are tired. You know that much. But there is a difference between needing a good night's sleep and being in a state that sleep cannot fix. This article helps you work out whi...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-who/" },
  { id: "burnout-escape-plan-workplace-2026", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Your Workplace Rights When You're Burning Out (UK 2026)", excerpt: "Most burnt-out professionals do not know what they are legally entitled to. They assume they have two options: push through or quit. In reality, UK employment law provides sever...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-workplace-2026/" },
  { id: "burnout-escape-plan-worth-it", date: "May 2026", category: "Burnout Recovery", rating: 4.8, title: "Is £8.99 Worth It When You're Already Stretched?", excerpt: "You are burnt out, you are likely watching every pound, and the idea of spending money on yet another self-help resource feels like a gamble you cannot afford to lose. This arti...", externalUrl: "https://reviews.alvaroabreu.com/burnout-escape-plan-worth-it/" },
  { id: "first-time-buyer-uk-before-you-buy", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "The First-Time Buyer's Cheat Sheet UK 2026: Chapter-by-Chapter Review — Is It Worth £8.99?", excerpt: "An honest, detailed assessment of what you get, what works, what doesn't, who should buy it, and who should save their money. No hype.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-before-you-buy/" },
  { id: "first-time-buyer-uk-beginners", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Buying Your First Home in the UK: A Beginner's Guide for People Who Know Nothing About the Process", excerpt: "You don't know what a LISA is. You're not sure what stamp duty means. You've never heard of conveyancing. That's fine. Everyone starts here. Let's make this simple.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-beginners/" },
  { id: "first-time-buyer-uk-checklist", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "The Complete First-Time Buyer Checklist for 2026", excerpt: "Buying your first home in the UK involves roughly 47 distinct tasks spread across six to nine months. Miss one — especially early on — and you could lose weeks or thousands of p...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-checklist/" },
  { id: "first-time-buyer-uk-complete-guide", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "The Complete Guide to Buying Your First Home in the UK in 2026", excerpt: "From the affordability check to the moment you turn the key in the front door. Everything a first-time buyer needs to know about the 2026 market — deposits, schemes, mortgages, ...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-complete-guide/" },
  { id: "first-time-buyer-uk-editorial", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "May 2026 Is the Best Time to Be a First-Time Buyer in a Decade. Here's Why.", excerpt: "An opinion piece. Four policy shifts have converged to create an unusually favourable window for UK renters ready to become owners. The window won't stay open indefinitely.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-editorial/" },
  { id: "first-time-buyer-uk-expert", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "What Mortgage Brokers Wish First-Time Buyers Knew Before Walking Through the Door", excerpt: "Mortgage brokers see hundreds of first-time buyers every year. They watch the same misunderstandings, the same timing errors, and the same preventable rejections play out again ...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-expert/" },
  { id: "first-time-buyer-uk-faq", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Can I Buy a House on a £30k Salary? 15 Questions Every First-Time Buyer Asks in 2026", excerpt: "The honest answers to every question you've been Googling at midnight. Real numbers, real schemes, real costs — no jargon, no waffle.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-faq/" },
  { id: "first-time-buyer-uk-free-vs-paid", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Gov.uk and MoneySavingExpert vs a Paid Cheat Sheet — What's Actually Different?", excerpt: "There's a mountain of free information for UK first-time buyers. Gov.uk, MoneySavingExpert, Which?, Citizens Advice, YouTube, Reddit — it's all out there. So why would anyone pa...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-free-vs-paid/" },
  { id: "first-time-buyer-uk-hidden-costs", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Every Hidden Cost of Buying Your First Home — The Real Numbers", excerpt: "Your deposit is the headline number. But between your accepted offer and collecting the keys, you'll spend an additional £5,000–£12,000 on costs that most first-time buyer guide...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-hidden-costs/" },
  { id: "first-time-buyer-uk-how-to", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "How to Buy Your First House in the UK: A Step-by-Step Guide for 2026", excerpt: "The problem is not a lack of information — it's that the information is scattered across 50 websites, half of it is American, and the rest was written in 2022. Here's the comple...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-how-to/" },
  { id: "first-time-buyer-uk-market-2026", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "What Changed in the UK Housing Market in 2026 — and What It Means for First-Time Buyers", excerpt: "2026 has been one of the most significant years for UK housing in a decade. New legislation, new mortgage products, and shifting market dynamics have fundamentally altered the l...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-market-2026/" },
  { id: "first-time-buyer-uk-mistakes", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "11 Mistakes That Cost First-Time Buyers Thousands — and How to Dodge Every One", excerpt: "Buying your first home in the UK should be exciting. Instead, most first-time buyers stumble into a minefield of avoidable errors — each one costing hundreds or thousands of pou...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-mistakes/" },
  { id: "first-time-buyer-uk-myths", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "10 Myths Keeping You Renting When You Could Be Buying in 2026", excerpt: "Every one of these beliefs is costing renters years of potential home ownership. Every one is wrong. Here's the proof, with real 2026 numbers.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-myths/" },
  { id: "first-time-buyer-uk-results", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "4 First-Time Buyers, 4 Different Incomes, 4 Paths to Ownership in 2026", excerpt: "A couple saving from scratch. A single earner on a modest salary. A Shared Ownership success. And someone whose survey saved them from a £12,000 disaster. Real scenarios, real n...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-results/" },
  { id: "first-time-buyer-uk-review", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "I Was a Renter on £31k With No Clue How to Buy. This £8.99 Guide Replaced 47 Browser Tabs.", excerpt: "A personal, no-filter review of The First-Time Buyer's Cheat Sheet: UK 2026 — the guide I wish existed when I started panicking about deposits, LISAs, and solicitor fees at 2 a.m.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-review/" },
  { id: "first-time-buyer-uk-savings", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Can an £8.99 Guide Really Save You Thousands? Let's Do the Maths", excerpt: "Every guide, course, and ebook claims it'll save you money. Most are vague about how. We're going to be specific. Here are nine concrete scenarios where information from The Fir...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-savings/" },
  { id: "first-time-buyer-uk-schemes", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Every Government Scheme for First-Time Buyers Decoded — 2026 Edition", excerpt: "The UK government offers several schemes designed to help first-time buyers get on the property ladder. The problem isn't that these schemes don't exist — it's that they're scat...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-schemes/" },
  { id: "first-time-buyer-uk-timeline", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Offer to Keys: The Exact Week-by-Week Timeline for UK First-Time Buyers", excerpt: "Your offer has been accepted. Congratulations — now what? The 12 to 16 weeks between 'offer accepted' and 'keys in hand' are the most opaque part of buying a home. Here is exact...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-timeline/" },
  { id: "first-time-buyer-uk-vs-alternatives", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Best First Time Buyer Guides UK 2026: 5 Resources Compared", excerpt: "MoneySavingExpert. Which?. A mortgage broker. Gov.uk. And a £8.99 ebook. We used all five while preparing to buy our first home. Here's how they stack up.", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-vs-alternatives/" },
  { id: "first-time-buyer-uk-who", date: "May 2026", category: "First-Time Buyer", rating: 4.8, title: "Are You Actually Ready to Buy Your First Home? 8 Honest Signals", excerpt: "Not everyone who wants to buy a home is ready to buy a home. That's not a judgement — it's arithmetic. Here are eight honest signals that separate 'I want to buy' from 'I'm genu...", externalUrl: "https://reviews.alvaroabreu.com/first-time-buyer-uk-who/" },
  { id: "uk-renting-with-pets-before-you-buy", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Before You Buy: Is the 'Renting with Pets 2026' Guide Actually Worth £8.99?", excerpt: "An honest walkthrough of what's inside, chapter by chapter. Who benefits most, who should save their money, what's genuinely good, and what we'd improve. No hype — just a clear ...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-before-you-buy/" },
  { id: "uk-renting-with-pets-beginners", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Never Asked Your Landlord About a Pet? Start Here.", excerpt: "This is the page for people who've never navigated this process before. No legal jargon. No assumptions about what you already know. Just clear answers to the questions you're p...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-beginners/" },
  { id: "uk-renting-with-pets-checklist", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "The Complete 2026 Checklist for Getting Your Landlord to Approve Your Pet", excerpt: "Under the Renters' Rights Act 2026, landlords in England can no longer blanket-ban pets. But getting approval still requires the right approach. Here is every step, in order, so...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-checklist/" },
  { id: "uk-renting-with-pets-complete-guide", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "The Complete Guide to Renting with Pets Under the Renters' Rights Act 2026", excerpt: "The most significant change to UK tenant-pet rights in decades came into force on 1 May 2026. This is everything you need to know — from the legal framework to the practical pro...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-complete-guide/" },
  { id: "uk-renting-with-pets-costs", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Every Cost of Renting with a Pet in the UK — Deposits, Insurance, and Hidden Fees (2026)", excerpt: "Before you send that request letter to your landlord, you need to know exactly what it will cost. Here is a transparent breakdown of every expense — from pet deposits and insura...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-costs/" },
  { id: "uk-renting-with-pets-editorial", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "The Renters' Rights Act Changed Everything for Pet Owners — But Most Renters Don't Know It Yet", excerpt: "On 1 May 2026, the balance of power between landlords and tenants shifted more dramatically than at any point since 1988. For pet owners, the implications are profound. For pet-...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-editorial/" },
  { id: "uk-renting-with-pets-expert", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "What Housing Experts Say About the New Pet Renting Rules", excerpt: "The Renters' Rights Act 2026 transformed the relationship between pet-owning tenants and landlords across England. But the legislation itself is dense, and the practical implica...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-expert/" },
  { id: "uk-renting-with-pets-faq", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Can My Landlord Refuse Pets in 2026? 15 Questions Every UK Renter Is Asking Right Now", excerpt: "The Renters' Rights Act changed the rules for pets in rented homes. But the questions haven't stopped — they've multiplied. Here are the 15 most common questions tenants are ask...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-faq/" },
  { id: "uk-renting-with-pets-free-vs-paid", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Free Gov.uk Advice vs a Paid Guide — What's the Real Difference?", excerpt: "This is a fair question, and it deserves an honest answer. There is a lot of free information available about renting with pets in the UK. Gov.uk publishes the legislation. Shel...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-free-vs-paid/" },
  { id: "uk-renting-with-pets-how-to", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "How to Ask Your Landlord for a Pet in the UK: A 5-Step Guide That Actually Works", excerpt: "You want a pet. Your landlord doesn't want you to have one. The law is now on your side — but only if you use it correctly. Here's the 5-step process that turns a stressful conv...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-how-to/" },
  { id: "uk-renting-with-pets-investment", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Is an £8.99 Guide Worth It When Free Info Exists?", excerpt: "This is the most important question a potential buyer can ask, and we respect you for asking it. Free information about renting with pets exists. Gov.uk, Shelter, Citizens Advic...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-investment/" },
  { id: "uk-renting-with-pets-law-2026", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "What the 2026 Renters' Rights Act Actually Changed for Pet Owners", excerpt: "The Renters' Rights Act 2026 is the most significant reform of private renting law in England in a generation. For pet-owning tenants, the changes are transformative. But much o...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-law-2026/" },
  { id: "uk-renting-with-pets-mistakes", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "9 Mistakes That Get Pet Requests Rejected by UK Landlords", excerpt: "The Renters' Rights Act 2026 means landlords can no longer blanket-ban pets. But that does not mean every request gets approved. According to Shelter, roughly one in three pet p...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-mistakes/" },
  { id: "uk-renting-with-pets-myths", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "8 Myths About Renting with Pets in the UK That Are Costing You in 2026", excerpt: "The Renters' Rights Act changed the rules. But myths move slower than legislation. Here are 8 beliefs about renting with pets that are now demonstrably false — and one that's ac...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-myths/" },
  { id: "uk-renting-with-pets-results", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "4 UK Tenants Used the Renters' Rights Act to Get Their Pets Approved — Here Are Their Stories", excerpt: "The Renters' Rights Act 2025 came into force on 1 May 2026. Within the first two weeks, tenants across England started testing its pet-request provisions. We followed four diffe...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-results/" },
  { id: "uk-renting-with-pets-review", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "I Used ‘Renting with Pets 2026’ to Get My Landlord to Approve My Cat — Here’s My Honest Review", excerpt: "After two rejections and a threat of eviction, I found a guide that walked me through the new Renters' Rights Act step by step. Two weeks later, my cat was officially on the ten...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-review/" },
  { id: "uk-renting-with-pets-templates", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Pet CV Templates and Landlord Letters That Actually Work", excerpt: "The most common question tenants ask about the pet request process is not about the law. It is about the paperwork. What exactly do you write? What goes into a Pet CV? How do yo...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-templates/" },
  { id: "uk-renting-with-pets-timeline", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Pet Rental Approval: The Exact Timeline From Request to Move-In", excerpt: "One of the most stressful parts of renting with a pet in the UK is not knowing how long the process takes. Will you have an answer in a week? A month? What happens if the landlo...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-timeline/" },
  { id: "uk-renting-with-pets-vs-alternatives", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Best UK Pet Renting Guides 2026: 5 Options Compared So You Don't Waste Time or Money", excerpt: "The Renters' Rights Act changed everything for pet owners in rented accommodation. But where should you actually go for help? We tested five resources — from free charity pages ...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-vs-alternatives/" },
  { id: "uk-renting-with-pets-who", date: "May 2026", category: "Renting with Pets", rating: 4.8, title: "Is This Guide Right for You? 6 Signs You Need It", excerpt: "Not every tenant needs a guide. If you have already navigated the pet request process successfully, or if your landlord is enthusiastically pet-friendly, you can probably manage...", externalUrl: "https://reviews.alvaroabreu.com/uk-renting-with-pets-who/" },
];

/* ─── i18n helpers for content data ───────────────────────── */

function getLocalizedNotes(lang) {
  if (lang === "en" || !window.NOTES_I18N || !window.NOTES_I18N[lang]) return NOTES;
  var tr = window.NOTES_I18N[lang];
  return NOTES.map(function(n) {
    var l = tr[n.id];
    if (!l) return n;
    return Object.assign({}, n, l);
  });
}

function getLocalizedReviews(lang) {
  if (lang === "en" || !window.REVIEWS_I18N || !window.REVIEWS_I18N[lang]) return REVIEWS;
  var tr = window.REVIEWS_I18N[lang];
  return REVIEWS.map(function(r) {
    var l = tr[r.id];
    if (!l) return r;
    return Object.assign({}, r, l);
  });
}

const Stars = ({ value }) => {
  const full = Math.floor(value); const half = value % 1 >= 0.5;
  return (
    <span style={stuStyles.stars}>
      {"★".repeat(full)}{half && "✧"}{"☆".repeat(5 - full - (half ? 1 : 0))}
      <span style={stuStyles.starsNum}>{value.toFixed(1)}</span>
    </span>
  );
};

const StudioReviews = ({ reviews, onReview, t, isMobile, isTablet }) => (
  <React.Fragment>
    <section style={{...stuStyles.indexHero, ...(isMobile ? { padding: "48px 16px 24px" } : isTablet ? { padding: "72px 32px 32px" } : {})}}>
      <div style={stuStyles.kicker}>{t("reviews")}</div>
      <h1 style={{...stuStyles.indexHeroTitle, ...(isMobile ? { fontSize: 32, letterSpacing: -0.8 } : isTablet ? { fontSize: 48, letterSpacing: -1.2 } : {})}}>{t("reviewsHeroTitle1")}<br/><span style={stuStyles.heroAccent}>{t("reviewsHeroTitle2")}</span></h1>
      <p style={stuStyles.indexHeroLede}>{t("reviewsHeroLede")}</p>
      <div style={stuStyles.disclosure}>
        <span style={stuStyles.disclosureDot}></span>
        <span><strong>{t("affDisclosure")}</strong> {t("affDisclosureText")}</span>
      </div>
    </section>

    <section style={{...stuStyles.reviewGrid, ...(isMobile ? { gridTemplateColumns: "1fr", padding: "24px 16px 60px" } : isTablet ? { padding: "32px 32px 80px" } : {})}}>
      {(reviews || REVIEWS).map((r) => (
        <article key={r.id} style={{...stuStyles.reviewCard, cursor: "pointer"}} onClick={() => r.externalUrl ? (window.location.href = r.externalUrl) : onReview(r.id)}>
          <div style={stuStyles.reviewTop}>
            <div style={stuStyles.reviewMeta}>
              <span>{r.date}</span><span style={stuStyles.indexDot}>·</span><span>{r.category}</span>
            </div>
            <Stars value={r.rating} />
          </div>
          <h2 style={stuStyles.reviewTitle}>{r.title}</h2>
          <p style={stuStyles.reviewExcerpt}>{r.excerpt}</p>
          <div style={stuStyles.reviewFoot}>
            <a style={stuStyles.indexLink} href={r.externalUrl || undefined} onClick={r.externalUrl ? (e) => { e.stopPropagation(); } : undefined}>{t("readTheReview")}</a>
            {r.affiliate && (
              <a style={stuStyles.affBtn} onClick={(e) => e.stopPropagation()}>
                {r.cta} <span style={stuStyles.affTag}>aff</span>
              </a>
            )}
          </div>
        </article>
      ))}
    </section>
  </React.Fragment>
);

const StudioReviewDetail = ({ review, reviews, onBack, onReview, t, isMobile, isTablet }) => {
  const r = review;
  const arr = reviews || REVIEWS;
  const currentIdx = arr.findIndex(x => x.id === r.id);
  const nextReview = arr[currentIdx + 1];
  const prevReview = currentIdx > 0 ? arr[currentIdx - 1] : null;

  return (
    <React.Fragment>
      <div style={{...stuStyles.crumb, ...(isMobile ? { padding: "20px 16px 0", flexWrap: "wrap" } : isTablet ? { padding: "24px 32px 0" } : {})}}>
        <a style={stuStyles.crumbLink} onClick={onBack}>← {t("reviews")}</a>
        <span style={stuStyles.crumbSep}>/</span>
        <span style={stuStyles.crumbCat}>{r.category}</span>
      </div>

      <article style={{...stuStyles.articleWrap, ...(isMobile ? { padding: "24px 16px 60px" } : isTablet ? { padding: "32px 32px 80px" } : {})}}>
        <header style={stuStyles.articleHeader}>
          <div style={stuStyles.kicker}>{r.category}</div>
          <h1 style={{...stuStyles.articleH1, ...(isMobile ? { fontSize: 30, letterSpacing: -0.6 } : isTablet ? { fontSize: 40, letterSpacing: -1 } : {})}}>{r.title}</h1>
          <div style={stuStyles.articleMeta}>
            <span>{r.date}</span>
            <span style={stuStyles.indexDot}>·</span>
            <Stars value={r.rating} />
            <span style={stuStyles.indexDot}>·</span>
            <span>{t("byAuthor")}</span>
          </div>
          <p style={stuStyles.articleLede}>{r.excerpt}</p>
          <div style={stuStyles.disclosure}>
            <span style={stuStyles.disclosureDot}></span>
            <span><strong>{t("affDisclosure")}</strong> {t("affDisclosureShort")}</span>
          </div>
        </header>

        <div style={stuStyles.articleBody}>
          {r.sections.map((s, i) => (
            <section key={i}>
              <h2 style={stuStyles.articleH2}>{s.heading}</h2>
              {s.body.split("\n\n").map((p, j) => (
                <p key={j} style={stuStyles.articleP}>{p}</p>
              ))}
            </section>
          ))}

          <div style={stuStyles.verdictBox}>
            <div style={stuStyles.kicker}>{t("theVerdict")}</div>
            <p style={stuStyles.verdictText}>{r.verdict}</p>
            {r.affiliate && (
              <a style={stuStyles.pdBuyBtn} href={r.affiliateUrl}>
                {r.cta} <span style={{...stuStyles.affTag, marginLeft: 8}}> aff</span>
              </a>
            )}
          </div>
        </div>

        <nav style={stuStyles.articleNav}>
          {prevReview && (
            <a style={stuStyles.articleNavLink} onClick={() => onReview(prevReview.id)}>
              <span style={stuStyles.articleNavDir}>{t("previous")}</span>
              <span style={stuStyles.articleNavTitle}>{prevReview.title}</span>
            </a>
          )}
          {!prevReview && <div />}
          {nextReview && (
            <a style={{...stuStyles.articleNavLink, textAlign: "right"}} onClick={() => onReview(nextReview.id)}>
              <span style={stuStyles.articleNavDir}>{t("next")}</span>
              <span style={stuStyles.articleNavTitle}>{nextReview.title}</span>
            </a>
          )}
        </nav>
      </article>
    </React.Fragment>
  );
};

/* ─── Contact view ──────────────────────────────────────────── */

const StudioContact = ({ t, isMobile, isTablet }) => (
  <React.Fragment>
    <section style={{...stuStyles.indexHero, ...(isMobile ? { padding: "48px 16px 24px" } : isTablet ? { padding: "72px 32px 32px" } : {})}}>
      <div style={stuStyles.kicker}>{t("contact")}</div>
      <h1 style={{...stuStyles.indexHeroTitle, ...(isMobile ? { fontSize: 32, letterSpacing: -0.8 } : isTablet ? { fontSize: 48, letterSpacing: -1.2 } : {})}}>{t("contactHeroTitle1")}<br/><span style={stuStyles.heroAccent}>{t("contactHeroTitle2")}</span></h1>
      <p style={stuStyles.indexHeroLede}>{t("contactHeroLede")}</p>
    </section>

    <section style={{...stuStyles.contactGrid, ...(isMobile ? { gridTemplateColumns: "1fr", padding: "24px 16px 60px", gap: 24 } : isTablet ? { gridTemplateColumns: "1fr", padding: "32px 32px 80px", gap: 28 } : {})}}>
      <div style={stuStyles.contactForm}>
        <div style={stuStyles.kicker}>{t("sendMessage")}</div>
        <h3 style={stuStyles.contactH3}>{t("tellMe")}</h3>
        <div style={stuStyles.formRow}>
          <label style={stuStyles.formLbl}>{t("yourName")}</label>
          <input style={stuStyles.formInput} placeholder="Alex Reader" />
        </div>
        <div style={stuStyles.formRow}>
          <label style={stuStyles.formLbl}>{t("email")}</label>
          <input style={stuStyles.formInput} placeholder="you@email.com" />
        </div>
        <div style={stuStyles.formRow}>
          <label style={stuStyles.formLbl}>{t("topicLabel")}</label>
          <select style={stuStyles.formInput}>
            <option>{t("topicQ")}</option>
            <option>{t("topicWrite")}</option>
            <option>{t("topicPartner")}</option>
            <option>{t("topicPress")}</option>
            <option>{t("topicOther")}</option>
          </select>
        </div>
        <div style={stuStyles.formRow}>
          <label style={stuStyles.formLbl}>{t("message")}</label>
          <textarea style={{...stuStyles.formInput, minHeight: 140, resize: "vertical"}} placeholder=""></textarea>
        </div>
        <button style={stuStyles.btnPrimary}>{t("sendBtn")}</button>
        <div style={stuStyles.formFine}>{t("formFine")}</div>
      </div>

      <aside style={stuStyles.contactAside}>
        <div style={stuStyles.contactBlock}>
          <div style={stuStyles.kicker}>{t("direct")}</div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>{t("email")}</span><span style={stuStyles.statVal}>support@alvaroabreu.com</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>{t("replyWithin")}</span><span style={stuStyles.statVal}>≤ 2 business days</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>{t("timezone")}</span><span style={stuStyles.statVal}>São Paulo · GMT-3</span></div>
        </div>

        <div style={stuStyles.contactBlock}>
          <div style={stuStyles.kicker}>{t("elsewhere")}</div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>LinkedIn</span><span style={stuStyles.statVal}>/in/alvaroabreu</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>Substack</span><span style={stuStyles.statVal}>alvaroabreu.substack</span></div>
          <div style={stuStyles.statDiv}></div>
          <div style={stuStyles.contactRow}><span style={stuStyles.statLbl}>X</span><span style={stuStyles.statVal}>@alvaroabreu</span></div>
        </div>

        <div style={stuStyles.contactBlock}>
          <div style={stuStyles.kicker}>{t("wontReply")}</div>
          <ul style={{margin: 0, paddingLeft: 20, fontSize: 14, color: "var(--stuMute)", lineHeight: 1.7}}>
            <li>{t("coldPitch")}</li>
            <li>{t("seoLink")}</li>
            <li>{t("quickCall")}</li>
          </ul>
        </div>
      </aside>
    </section>
  </React.Fragment>
);

/* ─── Tools view ────────────────────────────────────────────── */

const STUDIO_TOOLS = [
  {
    id: "t01",
    name: "Coming soon",
    tagline: "Practical tools, built for specific problems",
    description: "Small, focused utilities designed for professionals who need answers, not subscriptions. Each tool solves one problem well.",
    status: "building",
  },
];

const StudioTools = ({ t, isMobile, isTablet }) => (
  <React.Fragment>
    <section style={{...stuStyles.indexHero, ...(isMobile ? { padding: "48px 16px 24px" } : isTablet ? { padding: "72px 32px 32px" } : {})}}>
      <div style={stuStyles.kicker}>{t("tools")}</div>
      <h1 style={{...stuStyles.indexHeroTitle, ...(isMobile ? { fontSize: 32, letterSpacing: -0.8 } : isTablet ? { fontSize: 48, letterSpacing: -1.2 } : {})}}>{t("toolsHeroTitle1")}<br/><span style={stuStyles.heroAccent}>{t("toolsHeroTitle2")}</span></h1>
      <p style={stuStyles.indexHeroLede}>{t("toolsHeroLede")}</p>
    </section>

    <section style={{...stuStyles.toolsList, ...(isMobile ? { padding: "24px 16px 60px" } : isTablet ? { padding: "32px 32px 80px" } : {})}}>

      {/* ── AnesthesiaPro featured card ── */}
      <a href="https://tools.alvaroabreu.com/AnesthesiaPro/" target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none", color: "inherit", display: "block" }}>
        <div style={{...stuStyles.apCard, ...(isMobile ? { gridTemplateColumns: "1fr", gridTemplateRows: "auto auto" } : {})}}>
          {/* Left — dark cover */}
          <div style={{...stuStyles.apCover, ...(isMobile ? { padding: 28 } : {})}}>
            <div style={stuStyles.apLogo}>Ap</div>
            <div>
              <div style={stuStyles.apCoverKicker}>TOOL #01</div>
              <h2 style={{...stuStyles.apCoverTitle, ...(isMobile ? { fontSize: 32 } : {})}}>{t("apName")}</h2>
              <p style={stuStyles.apCoverTagline}>{t("apTagline")}</p>
            </div>
            <div style={stuStyles.apCoverFoot}>
              <span style={stuStyles.apBadgeEl}>{t("apBadge")}</span>
              <span style={stuStyles.apLangEl}>{t("apLang")}</span>
            </div>
          </div>
          {/* Right — features */}
          <div style={{...stuStyles.apBody, ...(isMobile ? { padding: 24 } : {})}}>
            <p style={stuStyles.apBlurb}>{t("apBlurb")}</p>
            <div style={{...stuStyles.apFeats, ...(isMobile ? { gridTemplateColumns: "1fr" } : {})}}>
              <div style={stuStyles.apFeat}>
                <div style={stuStyles.apFeatIcon}>💧</div>
                <div><div style={stuStyles.apFeatTitle}>{t("apFeat1")}</div><div style={stuStyles.apFeatDesc}>{t("apFeat1d")}</div></div>
              </div>
              <div style={stuStyles.apFeat}>
                <div style={stuStyles.apFeatIcon}>💊</div>
                <div><div style={stuStyles.apFeatTitle}>{t("apFeat2")}</div><div style={stuStyles.apFeatDesc}>{t("apFeat2d")}</div></div>
              </div>
              <div style={stuStyles.apFeat}>
                <div style={stuStyles.apFeatIcon}>🫁</div>
                <div><div style={stuStyles.apFeatTitle}>{t("apFeat3")}</div><div style={stuStyles.apFeatDesc}>{t("apFeat3d")}</div></div>
              </div>
              <div style={stuStyles.apFeat}>
                <div style={stuStyles.apFeatIcon}>🎯</div>
                <div><div style={stuStyles.apFeatTitle}>{t("apFeat4")}</div><div style={stuStyles.apFeatDesc}>{t("apFeat4d")}</div></div>
              </div>
            </div>
            <div style={stuStyles.apTimerRow}>
              <span style={stuStyles.apTimerIcon}>⏱</span>
              <span style={stuStyles.apTimerTxt}>{t("apTimer")}</span>
            </div>
            <div style={stuStyles.apCtaRow}>
              <span style={stuStyles.apCtaBtn}>{t("apCta")}</span>
            </div>
          </div>
        </div>
      </a>

      <div style={{...stuStyles.subCard, marginTop: 32}}>
        <div style={stuStyles.subKicker}>{t("notifyLaunch2")}</div>
        <h3 style={stuStyles.subTitle}>{t("nextTwoShip")}</h3>
        <p style={stuStyles.subBody}>{t("notifyBody")}</p>
        <div style={stuStyles.subForm}>
          <input style={stuStyles.subInput} placeholder="your@email.com" />
          <button style={stuStyles.subBtn}>{t("notifyMe")}</button>
        </div>
      </div>
    </section>
  </React.Fragment>
);

/* ─── Success view (post-purchase thank you) ────────────────── */

const StudioSuccess = ({ product, products, onHome, onProduct, t, isMobile, isTablet }) => {
  const p = product;
  const related = products.filter(x => x.category === p.category && x.id !== p.id).slice(0, 3);
  const padX = isMobile ? 16 : isTablet ? 32 : 48;

  return (
    <React.Fragment>
      <section style={{ padding: `80px ${padX}px 40px`, maxWidth: 720, margin: "0 auto", textAlign: "center" }}>
        <div style={{ width: 64, height: 64, borderRadius: "50%", background: "var(--stuAccentSoft)", display: "grid", placeItems: "center", margin: "0 auto 24px", fontSize: 32 }}>✓</div>
        <div style={{ ...stuStyles.kicker, marginBottom: 12 }}>{t("successKicker")}</div>
        <h1 style={{ fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: isMobile ? 32 : 48, lineHeight: 1.06, letterSpacing: -1, margin: "0 0 20px" }}>
          {t("successTitle")}
        </h1>
        <p style={{ fontSize: 18, lineHeight: 1.6, color: "var(--stuBlurb)", maxWidth: 560, margin: "0 auto 32px" }}>
          {t("successSub")}
        </p>
        <div style={{ background: "var(--stuCard)", border: `1px solid var(--stuRule)`, borderRadius: 14, padding: 28, textAlign: "left", marginBottom: 32 }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
            <div>
              <div style={{ fontFamily: '"Fraunces", Georgia, serif', fontSize: 22, letterSpacing: -0.3 }}>{p.title}</div>
              <div style={{ fontSize: 13, color: "var(--stuMute)", marginTop: 4 }}>{p.category} · {p.market}</div>
            </div>
            <div style={{ fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 14 }}>{p.price}</div>
          </div>
          <div style={{ height: 1, background: "var(--stuRule)", marginBottom: 16 }}></div>
          <div style={{ fontSize: 14, color: "var(--stuMute)", lineHeight: 1.6 }}>
            {t("successDelivery")}
          </div>
        </div>
        <div style={{ display: "flex", gap: 12, justifyContent: "center", flexWrap: "wrap" }}>
          <button style={stuStyles.btnPrimary} onClick={onHome}>{t("successBrowse")}</button>
        </div>
      </section>

      {related.length > 0 && (
        <section style={{ padding: `40px ${padX}px 80px`, maxWidth: 1320, margin: "0 auto" }}>
          <div style={{ ...stuStyles.kicker, marginBottom: 12 }}>{t("readNext")}</div>
          <h2 style={{ fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: isMobile ? 24 : 32, letterSpacing: -0.6, margin: "0 0 24px" }}>{t("successRelated")}</h2>
          <div style={{ display: "grid", gridTemplateColumns: isMobile ? "1fr" : isTablet ? "repeat(2,1fr)" : "repeat(3,1fr)", gap: 20 }}>
            {related.map(r => (
              <div key={r.id} style={stuStyles.card} onClick={() => onProduct(r.id)}>
                <div style={stuStyles.cardBody}>
                  <div style={stuStyles.cardTop}>
                    <span style={stuStyles.cardCat}>{r.category}</span>
                    <span style={stuStyles.cardMarket}>{r.market}</span>
                  </div>
                  <h3 style={stuStyles.cardTitle}>{r.title}</h3>
                  <p style={stuStyles.cardBlurb}>{r.subtitle}</p>
                  <div style={stuStyles.cardFoot}>
                    <span style={stuStyles.cardPrice}>{r.price}</span>
                    <span style={stuStyles.cardCta}>{t("viewArrow")}</span>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </section>
      )}
    </React.Fragment>
  );
};

/* ─── Styles ────────────────────────────────────────────────── */

const stuInk    = "var(--stuInk)";
const stuPaper  = "var(--stuPaper)";
const stuCard   = "var(--stuCard)";
const stuMute   = "var(--stuMute)";
const stuRule   = "var(--stuRule)";
const stuAccent = "var(--stuAccent)";
const stuAccentSoft = "var(--stuAccentSoft)";

const stuStyles = {
  page: { fontFamily: '"Inter", system-ui, sans-serif', background: stuPaper, color: stuInk, width: "100%", minHeight: "100%", fontSize: 15, lineHeight: 1.55 },

  /* Header / footer */
  header: { background: stuPaper, borderBottom: `1px solid ${stuRule}`, position: "sticky", top: 0, zIndex: 10 },
  headerInner: { display: "grid", gridTemplateColumns: "1fr auto 1fr", alignItems: "center", padding: "20px 48px", maxWidth: 1320, margin: "0 auto" },
  brand: { display: "flex", alignItems: "center", gap: 14, cursor: "pointer" },
  brandBlock: { width: 40, height: 40, background: stuInk, color: stuPaper, display: "grid", placeItems: "center", borderRadius: 4, fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 28, lineHeight: 1, letterSpacing: -1 },
  brandStack: { display: "flex", flexDirection: "column", lineHeight: 1.1 },
  brandStackTop: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 18, letterSpacing: -0.3 },
  brandStackBot: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 9, letterSpacing: 2.2, textTransform: "uppercase", color: stuMute, marginTop: 4 },
  nav: { display: "flex", gap: 32 },
  navLink: { color: stuInk, fontSize: 14, cursor: "pointer", paddingBottom: 4, borderBottom: "1px solid transparent", textDecoration: "none" },
  navLinkActive: { borderBottom: `1px solid ${stuInk}` },
  headerRight: { display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 16 },
  langSwitch: { display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", border: `1px solid ${stuRule}`, borderRadius: 999, fontSize: 12 },
  langActive: { color: stuInk, fontWeight: 600 },
  lang: { color: stuMute, cursor: "pointer" },
  langDot: { color: stuRule },

  /* Hero */
  hero: { display: "grid", gridTemplateColumns: "1.5fr 1fr", gap: 80, padding: "100px 48px", maxWidth: 1320, margin: "0 auto", alignItems: "center" },
  heroLeft: {},
  statusPill: { display: "inline-flex", alignItems: "center", gap: 8, padding: "6px 14px", background: stuAccentSoft, color: stuAccent, borderRadius: 999, fontSize: 12, letterSpacing: 0.2, marginBottom: 32 },
  statusDot: { width: 6, height: 6, borderRadius: "50%", background: stuAccent },
  heroTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 76, lineHeight: 1.02, letterSpacing: -2, margin: 0, marginBottom: 28 },
  heroAccent: { fontStyle: "italic", color: stuAccent },
  heroSub: { fontSize: 18, lineHeight: 1.6, color: "var(--heroSubColor)", maxWidth: 540, margin: 0, marginBottom: 36 },
  heroCtas: { display: "flex", gap: 16, alignItems: "center" },
  btnPrimary: { background: stuInk, color: stuPaper, padding: "14px 24px", borderRadius: 8, fontSize: 14, cursor: "pointer", textDecoration: "none", display: "inline-block" },
  btnGhost: { color: stuInk, fontSize: 14, cursor: "pointer", textDecoration: "none" },
  heroRight: { display: "flex", justifyContent: "flex-end" },
  statCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 12, padding: "8px 24px", width: "100%", maxWidth: 360 },
  statRow: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "16px 0" },
  statLbl: { fontSize: 13, color: stuMute },
  statVal: { fontSize: 14, fontFamily: '"JetBrains Mono", ui-monospace, monospace', color: stuInk },
  statDiv: { height: 1, background: stuRule },

  /* Featured spread */
  feature: { padding: "80px 48px", maxWidth: 1320, margin: "0 auto" },
  featHead: { marginBottom: 40 },
  kicker: { fontSize: 11, letterSpacing: 1.5, textTransform: "uppercase", color: stuAccent, marginBottom: 12 },
  featTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 44, letterSpacing: -1, margin: 0 },
  featSpread: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 32, alignItems: "stretch" },
  featHeroCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 16, overflow: "hidden", display: "grid", gridTemplateColumns: "1fr 1fr", cursor: "pointer" },
  featHeroCover: { background: "var(--coverBg)", backgroundImage: `repeating-linear-gradient(135deg, transparent 0, transparent 12px, rgba(0,0,0,0.025) 12px, rgba(0,0,0,0.025) 13px)`, padding: 28, display: "flex" },
  featHeroFrame: { flex: 1, background: stuPaper, border: `1px solid ${stuRule}`, padding: 24, display: "flex", flexDirection: "column", justifyContent: "space-between" },
  featHeroNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11, color: stuMute, letterSpacing: 1 },
  featHeroEyebrow: { fontSize: 10, letterSpacing: 1, textTransform: "uppercase", color: stuMute, marginBottom: 10 },
  featHeroTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 36, lineHeight: 1.05, letterSpacing: -0.6 },
  featHeroMeta: { display: "flex", justifyContent: "space-between", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, color: stuMute, letterSpacing: 1, paddingTop: 14, borderTop: `1px solid ${stuRule}` },
  featHeroBody: { padding: 36, display: "flex", flexDirection: "column" },
  featCat: { fontSize: 12, color: stuMute, marginBottom: 12 },
  featHeroH3: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 32, lineHeight: 1.1, letterSpacing: -0.6, margin: "0 0 16px" },
  featHeroBlurb: { fontSize: 15.5, color: "var(--heroSubColor)", lineHeight: 1.6, margin: "0 0 28px", flex: 1 },
  featFoot: { display: "flex", justifyContent: "space-between", alignItems: "center", paddingTop: 16, borderTop: `1px solid ${stuRule}` },
  featPrice: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 14 },
  featLink: { fontSize: 13, color: stuAccent, cursor: "pointer" },

  featSmallCol: { display: "flex", flexDirection: "column", gap: 32 },
  featSmallCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, overflow: "hidden", display: "grid", gridTemplateColumns: "120px 1fr", cursor: "pointer", flex: 1 },
  featSmallCover: { background: "var(--coverBg)", backgroundImage: `repeating-linear-gradient(135deg, transparent 0, transparent 10px, rgba(0,0,0,0.025) 10px, rgba(0,0,0,0.025) 11px)`, padding: 12, display: "flex" },
  featSmallFrame: { flex: 1, background: stuPaper, border: `1px solid ${stuRule}`, padding: 12, display: "flex", flexDirection: "column", justifyContent: "space-between" },
  featSmallNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 9, color: stuMute, letterSpacing: 1 },
  featSmallTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 14, lineHeight: 1.1, letterSpacing: -0.2 },
  featSmallMeta: { display: "flex", justifyContent: "space-between", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 8, color: stuMute, letterSpacing: 1 },
  featSmallBody: { padding: 20, display: "flex", flexDirection: "column" },
  featSmallH4: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 22, lineHeight: 1.15, letterSpacing: -0.3, margin: "0 0 8px" },
  featSmallBlurb: { fontSize: 13.5, color: "var(--blurbColor)", lineHeight: 1.5, margin: "0 0 14px", flex: 1 },

  /* Catalog */
  cat: { padding: "80px 48px", maxWidth: 1320, margin: "0 auto" },
  catHead: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 32 },
  catTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 44, letterSpacing: -1, margin: 0 },
  catCount: { textAlign: "right" },
  catCountNum: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 32, marginRight: 8 },
  catCountLbl: { fontSize: 13, color: stuMute },
  filterStack: { display: "flex", flexDirection: "column", gap: 12, marginBottom: 32 },
  filterRow: { display: "grid", gridTemplateColumns: "80px 1fr", alignItems: "center", gap: 16 },
  filterLabel: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11, letterSpacing: 1.2, textTransform: "uppercase", color: stuMute },
  tabs: { display: "flex", gap: 4, padding: 4, background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 12, width: "fit-content", flexWrap: "wrap" },
  tab: { background: "transparent", border: "none", padding: "10px 18px", fontSize: 13, cursor: "pointer", borderRadius: 8, color: stuMute, fontFamily: "inherit" },
  tabMarket: { display: "flex", alignItems: "center", gap: 8, paddingLeft: 12 },
  tabFlag: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, letterSpacing: 1, padding: "3px 6px", background: stuPaper, border: `1px solid ${stuRule}`, borderRadius: 4, color: stuMute },
  tabFlagActive: { background: "rgba(255,255,255,0.12)", color: stuPaper, borderColor: "rgba(255,255,255,0.2)" },
  tabActive: { background: stuInk, color: stuPaper },
  grid: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 24 },
  card: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, overflow: "hidden", display: "flex", flexDirection: "column", cursor: "pointer" },
  cardCover: { aspectRatio: "5 / 3", background: "var(--coverBg)", backgroundImage: `repeating-linear-gradient(135deg, transparent 0, transparent 12px, rgba(0,0,0,0.025) 12px, rgba(0,0,0,0.025) 13px)`, padding: 18, display: "flex" },
  cardCoverInner: { flex: 1, background: stuPaper, border: `1px solid ${stuRule}`, padding: 14, display: "flex", flexDirection: "column", justifyContent: "space-between" },
  cardCoverCat: { fontSize: 10, letterSpacing: 1, textTransform: "uppercase", color: stuMute },
  cardCoverTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 18, lineHeight: 1.15, letterSpacing: -0.2, margin: "8px 0" },
  cardCoverFoot: { display: "flex", justifyContent: "space-between", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 9, color: stuMute, letterSpacing: 1 },
  cardBody: { padding: 20, flex: 1, display: "flex", flexDirection: "column" },
  cardTop: { display: "flex", justifyContent: "space-between", fontSize: 11, color: stuMute, letterSpacing: 0.4, textTransform: "uppercase", marginBottom: 10 },
  cardCat: {},
  cardMarket: { fontFamily: '"JetBrains Mono", ui-monospace, monospace' },
  cardTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 20, lineHeight: 1.2, letterSpacing: -0.2, margin: "0 0 8px" },
  cardBlurb: { fontSize: 13.5, color: "var(--blurbColor)", lineHeight: 1.5, margin: "0 0 16px", flex: 1 },
  cardFoot: { display: "flex", justifyContent: "space-between", alignItems: "center", paddingTop: 14, borderTop: `1px solid ${stuRule}` },
  cardPrice: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 13 },
  cardCta: { fontSize: 12, color: stuAccent },

  /* Lower */
  lower: { display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 32, padding: "80px 48px", maxWidth: 1320, margin: "0 auto" },
  aboutCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 16, padding: 48 },
  aboutTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 36, lineHeight: 1.1, letterSpacing: -0.6, margin: "0 0 24px" },
  aboutP: { fontSize: 16, color: "var(--heroSubColor)", lineHeight: 1.65, margin: "0 0 16px" },
  aboutLink: { fontSize: 14, color: stuAccent, cursor: "pointer", display: "inline-block", marginTop: 8 },

  subCard: { background: stuInk, color: stuPaper, borderRadius: 16, padding: 40, display: "flex", flexDirection: "column" },
  subKicker: { fontSize: 11, letterSpacing: 1.5, textTransform: "uppercase", color: "var(--subKickerColor)", marginBottom: 14 },
  subTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 28, lineHeight: 1.15, letterSpacing: -0.4, margin: "0 0 14px" },
  subBody: { fontSize: 14.5, lineHeight: 1.6, color: "var(--subBodyColor)", margin: "0 0 28px", flex: 1 },
  subForm: { display: "flex", background: "rgba(255,255,255,0.06)", borderRadius: 8, padding: 4, border: "1px solid rgba(255,255,255,0.1)" },
  subInput: { flex: 1, background: "transparent", border: "none", padding: "10px 14px", color: stuPaper, fontSize: 14, fontFamily: "inherit", outline: "none" },
  subBtn: { background: stuPaper, color: stuInk, border: "none", padding: "10px 18px", borderRadius: 6, fontSize: 13, cursor: "pointer", fontFamily: "inherit" },
  subFine: { fontSize: 12, color: "var(--subFineColor)", marginTop: 14 },

  /* Footer */
  footer: { borderTop: `1px solid ${stuRule}`, padding: "48px 48px 32px", background: stuPaper },
  footerInner: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", maxWidth: 1320, margin: "0 auto 32px" },
  footerL: {},
  footerBrand: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 24, marginBottom: 4 },
  footerBrandLockup: { display: "inline-flex", alignItems: "center", gap: 18 },
  footerTag: { fontSize: 13, color: stuMute },
  footerR: { display: "flex", gap: 28 },
  footerLink: { fontSize: 13.5, color: stuInk, cursor: "pointer", textDecoration: "none" },
  footerBot: { display: "flex", justifyContent: "space-between", fontSize: 12, color: stuMute, paddingTop: 24, borderTop: `1px solid ${stuRule}`, maxWidth: 1320, margin: "0 auto" },

  /* Product detail */
  crumb: { maxWidth: 1320, margin: "0 auto", padding: "32px 48px 0", display: "flex", alignItems: "center", gap: 12, fontSize: 13 },
  crumbLink: { color: stuInk, cursor: "pointer" },
  crumbSep: { color: stuRule },
  crumbCat: { color: stuMute },
  crumbCur: { color: stuInk },

  pdTop: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 64, padding: "60px 48px 80px", maxWidth: 1320, margin: "0 auto", alignItems: "start" },
  pdCover: { aspectRatio: "3 / 4", background: "var(--coverBg)", backgroundImage: `repeating-linear-gradient(135deg, transparent 0, transparent 16px, rgba(0,0,0,0.04) 16px, rgba(0,0,0,0.04) 17px)`, padding: 28, display: "flex", borderRadius: 4, boxShadow: "8px 10px 0 rgba(26,31,28,0.08)" },
  pdCoverInner: { flex: 1, background: stuPaper, border: `1px solid ${stuRule}`, padding: 28, display: "flex", flexDirection: "column", justifyContent: "space-between" },
  pdCoverNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11, color: stuMute, letterSpacing: 1 },
  pdCoverEyebrow: { fontSize: 11, letterSpacing: 1, textTransform: "uppercase", color: stuMute, marginBottom: 12 },
  pdCoverTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 44, lineHeight: 1.05, letterSpacing: -1 },
  pdCoverFoot: { display: "flex", justifyContent: "space-between", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, color: stuMute, letterSpacing: 1, paddingTop: 14, borderTop: `1px solid ${stuRule}` },

  pdInfo: {},
  pdEyebrow: { fontSize: 12, color: stuAccent, letterSpacing: 0.4, marginBottom: 14, textTransform: "uppercase", fontFamily: '"JetBrains Mono", ui-monospace, monospace' },
  pdTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 56, lineHeight: 1.02, letterSpacing: -1.5, margin: "0 0 20px" },
  pdLede: { fontSize: 18, lineHeight: 1.55, color: "var(--heroSubColor)", margin: "0 0 32px" },
  pdBuy: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: 24, background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 12, marginBottom: 24 },
  pdBuyLeft: {},
  pdBuyPrice: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 34, lineHeight: 1, letterSpacing: -0.6 },
  pdBuyTax: { fontSize: 12, color: stuMute, marginTop: 6 },
  pdBuyBtn: { background: stuInk, color: stuPaper, padding: "14px 22px", borderRadius: 8, fontSize: 14, cursor: "pointer", textDecoration: "none", display: "inline-block" },
  pdMeta: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 12, padding: "8px 24px" },
  pdMetaRow: { display: "flex", justifyContent: "space-between", padding: "12px 0", borderBottom: `1px solid ${stuRule}` },
  pdMetaK: { fontSize: 13, color: stuMute },
  pdMetaV: { fontSize: 13, fontFamily: '"JetBrains Mono", ui-monospace, monospace' },

  pdSection: { padding: "80px 48px", maxWidth: 1320, margin: "0 auto" },
  pdSectionHead: { marginBottom: 40 },
  pdSectionTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 44, letterSpacing: -1, lineHeight: 1.05, margin: 0 },
  chapList: { listStyle: "none", padding: 0, margin: 0, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0, border: `1px solid ${stuRule}`, borderRadius: 12, overflow: "hidden", background: stuCard },
  chapItem: { display: "grid", gridTemplateColumns: "60px 1fr", alignItems: "center", padding: "20px 24px", borderBottom: `1px solid ${stuRule}`, borderRight: `1px solid ${stuRule}` },
  chapNum: { fontFamily: '"Fraunces", Georgia, serif', fontStyle: "italic", fontSize: 22, color: stuAccent },
  chapTxt: { fontSize: 15.5, fontFamily: '"Fraunces", Georgia, serif' },

  pdSplit: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 32, padding: "0 48px 80px", maxWidth: 1320, margin: "0 auto" },
  pdSplitCol: { padding: 36, background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14 },
  pdSplitH: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 24, marginBottom: 18, color: stuAccent },
  pdSplitHMute: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 24, marginBottom: 18, color: stuMute },
  pdSplitList: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 14 },
  pdSplitItem: { fontSize: 15, lineHeight: 1.55, paddingLeft: 24, position: "relative", color: "var(--splitItemColor)" },
  pdSplitItemMute: { fontSize: 15, lineHeight: 1.55, paddingLeft: 24, position: "relative", color: stuMute },

  pdCta: { padding: "80px 48px", maxWidth: 1320, margin: "0 auto" },
  pdCtaInner: { background: stuInk, color: stuPaper, padding: "60px 48px", borderRadius: 16, textAlign: "center" },
  pdCtaTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 44, letterSpacing: -1, margin: "0 0 16px" },
  pdCtaSub: { fontSize: 15, color: "var(--ctaSubColor)", margin: "0 0 28px" },

  pdRelated: { padding: "60px 48px 100px", maxWidth: 1320, margin: "0 auto" },

  /* About page */
  aboutHero: { padding: "100px 48px 60px", maxWidth: 980, margin: "0 auto" },
  aboutHeroTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 72, lineHeight: 1.02, letterSpacing: -2, margin: "16px 0 28px" },
  aboutHeroLede: { fontSize: 19, lineHeight: 1.6, color: "var(--heroSubColor)", maxWidth: 720, margin: 0 },

  aboutGrid: { display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 64, padding: "60px 48px", maxWidth: 1320, margin: "0 auto", alignItems: "start" },
  aboutTextCol: {},
  aboutH3: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 28, letterSpacing: -0.5, margin: "32px 0 14px" },
  aboutList: { paddingLeft: 22, margin: "0 0 16px", color: "var(--heroSubColor)" },

  aboutSide: { display: "flex", flexDirection: "column", gap: 24, position: "sticky", top: 100 },
  aboutSideCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, padding: 28 },
  aboutFacts: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24, marginTop: 18 },
  aboutFact: {},
  aboutFactN: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 44, lineHeight: 1, letterSpacing: -1 },
  aboutFactL: { fontSize: 12, color: stuMute, marginTop: 6, letterSpacing: 0.4, textTransform: "uppercase" },

  aboutSideMeta: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, padding: "8px 24px" },
  aboutSideRow: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "16px 0" },

  aboutCloser: { padding: "60px 48px 120px", maxWidth: 980, margin: "0 auto", textAlign: "center" },
  aboutCloserTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 48, lineHeight: 1.05, letterSpacing: -1.2, margin: "0 0 28px" },
};

/* Notes / Reviews / Contact extra styles */
Object.assign(stuStyles, {
  indexHero: { padding: "100px 48px 40px", maxWidth: 980, margin: "0 auto" },
  indexHeroTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 64, lineHeight: 1.04, letterSpacing: -1.6, margin: "16px 0 24px" },
  indexHeroLede: { fontSize: 18, lineHeight: 1.6, color: "var(--heroSubColor)", maxWidth: 680, margin: 0 },

  indexList: { padding: "40px 48px 100px", maxWidth: 980, margin: "0 auto" },
  indexItem: { display: "grid", gridTemplateColumns: "60px 1fr", gap: 32, padding: "36px 0", borderTop: `1px solid ${stuRule}` },
  indexNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 12, color: stuMute, paddingTop: 6 },
  indexBody: {},
  indexMeta: { display: "flex", alignItems: "center", gap: 8, fontSize: 12, color: stuMute, fontFamily: '"JetBrains Mono", ui-monospace, monospace', letterSpacing: 0.4, marginBottom: 14 },
  indexDot: { color: stuRule },
  indexTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 32, lineHeight: 1.12, letterSpacing: -0.6, margin: "0 0 14px" },
  indexExcerpt: { fontSize: 16, lineHeight: 1.6, color: "var(--heroSubColor)", margin: "0 0 16px", maxWidth: 680 },
  indexLink: { fontSize: 13.5, color: stuAccent, cursor: "pointer" },

  disclosure: { display: "flex", alignItems: "flex-start", gap: 12, padding: "16px 20px", background: stuAccentSoft, color: "var(--disclosureColor)", borderRadius: 10, fontSize: 13.5, lineHeight: 1.5, marginTop: 32, maxWidth: 720 },
  disclosureDot: { width: 8, height: 8, borderRadius: "50%", background: stuAccent, flexShrink: 0, marginTop: 6 },

  reviewGrid: { display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 24, padding: "40px 48px 100px", maxWidth: 1320, margin: "0 auto" },
  reviewCard: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, padding: 32, display: "flex", flexDirection: "column" },
  reviewTop: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 },
  reviewMeta: { display: "flex", alignItems: "center", gap: 8, fontSize: 12, color: stuMute, fontFamily: '"JetBrains Mono", ui-monospace, monospace', letterSpacing: 0.4 },
  reviewTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 26, lineHeight: 1.15, letterSpacing: -0.4, margin: "0 0 12px" },
  reviewExcerpt: { fontSize: 15, lineHeight: 1.6, color: "var(--heroSubColor)", margin: "0 0 24px", flex: 1 },
  reviewFoot: { display: "flex", justifyContent: "space-between", alignItems: "center", paddingTop: 16, borderTop: `1px solid ${stuRule}`, gap: 12 },
  affBtn: { display: "inline-flex", alignItems: "center", gap: 8, background: stuInk, color: stuPaper, padding: "8px 14px", borderRadius: 6, fontSize: 13, cursor: "pointer", textDecoration: "none" },
  affTag: { fontSize: 9, padding: "2px 6px", background: "rgba(255,255,255,0.15)", borderRadius: 3, letterSpacing: 0.5, textTransform: "uppercase" },
  stars: { color: stuAccent, fontSize: 14, letterSpacing: 1, display: "flex", alignItems: "center", gap: 8 },
  starsNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 12, color: stuMute, letterSpacing: 0 },

  contactGrid: { display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 48, padding: "40px 48px 100px", maxWidth: 1320, margin: "0 auto", alignItems: "start" },
  contactForm: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 16, padding: 40 },
  contactH3: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 30, letterSpacing: -0.5, margin: "8px 0 28px" },
  formRow: { display: "flex", flexDirection: "column", gap: 8, marginBottom: 18 },
  formLbl: { fontSize: 12, color: stuMute, letterSpacing: 0.4, textTransform: "uppercase", fontFamily: '"JetBrains Mono", ui-monospace, monospace' },
  formInput: { padding: "12px 14px", border: `1px solid ${stuRule}`, borderRadius: 8, background: stuPaper, fontSize: 14, fontFamily: "inherit", color: stuInk, outline: "none" },
  formFine: { fontSize: 12, color: stuMute, marginTop: 14 },

  contactAside: { display: "flex", flexDirection: "column", gap: 20 },
  contactBlock: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14, padding: "20px 24px" },
  contactRow: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "12px 0" },

  toolsList: { padding: "40px 48px 100px", maxWidth: 1100, margin: "0 auto" },
  toolGroup: { borderTop: `1px solid ${stuRule}`, padding: "36px 0" },
  toolGroupHead: { display: "grid", gridTemplateColumns: "60px 1fr auto", alignItems: "center", gap: 24, marginBottom: 20 },
  toolGroupNum: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 12, color: stuMute, letterSpacing: 1 },
  toolGroupTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 28, letterSpacing: -0.5, margin: 0 },
  toolGroupCount: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11, color: stuMute, letterSpacing: 0.6 },
  toolRows: { display: "flex", flexDirection: "column", gap: 0 },
  toolRow: { display: "grid", gridTemplateColumns: "60px 1.6fr 1fr 90px", alignItems: "center", gap: 24, padding: "14px 0 14px 60px", borderBottom: `1px solid ${stuRule}` },
  toolName: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 18, gridColumn: "2 / 3" },
  toolUse: { fontSize: 14, color: "var(--heroSubColor)" },
  toolStatus: { display: "inline-block", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, padding: "4px 10px", borderRadius: 999, letterSpacing: 1, textTransform: "uppercase", textAlign: "center", justifySelf: "end" },
  toolsLegend: { marginTop: 60, padding: 32, background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14 },
  legendRows: { display: "flex", flexDirection: "column", gap: 14, marginTop: 14 },
  legendRow: { display: "grid", gridTemplateColumns: "90px 1fr", alignItems: "center", gap: 16, fontSize: 14, color: "var(--heroSubColor)" },

  /* AnesthesiaPro card */
  apCard: { display: "grid", gridTemplateColumns: "380px 1fr", background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 16, overflow: "hidden", transition: "transform .15s, box-shadow .15s", cursor: "pointer" },
  apCover: { background: "#0f1419", color: "#fff", padding: 36, display: "flex", flexDirection: "column", justifyContent: "space-between", gap: 24, minHeight: 360 },
  apLogo: { width: 48, height: 48, borderRadius: 12, background: "linear-gradient(135deg, #3b82f6, #06b6d4)", display: "grid", placeItems: "center", fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 16, fontWeight: 700, color: "#fff", letterSpacing: -0.5 },
  apCoverKicker: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, letterSpacing: 2, color: "rgba(255,255,255,0.45)", marginBottom: 8 },
  apCoverTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 40, lineHeight: 1.05, letterSpacing: -1, margin: "0 0 8px" },
  apCoverTagline: { fontSize: 15, color: "rgba(255,255,255,0.6)", lineHeight: 1.5, margin: 0, fontStyle: "italic" },
  apCoverFoot: { display: "flex", justifyContent: "space-between", alignItems: "center", paddingTop: 16, borderTop: "1px solid rgba(255,255,255,0.1)" },
  apBadgeEl: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11, letterSpacing: 1, padding: "5px 12px", borderRadius: 999, background: "rgba(52,211,153,0.15)", color: "#34d399" },
  apLangEl: { fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 10, letterSpacing: 0.8, color: "rgba(255,255,255,0.4)" },
  apBody: { padding: 36, display: "flex", flexDirection: "column" },
  apBlurb: { fontSize: 15.5, lineHeight: 1.65, color: "var(--heroSubColor)", margin: "0 0 24px" },
  apFeats: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20, marginBottom: 20 },
  apFeat: { display: "flex", gap: 12, alignItems: "flex-start" },
  apFeatIcon: { width: 36, height: 36, background: stuAccentSoft, borderRadius: 8, display: "grid", placeItems: "center", fontSize: 16, flexShrink: 0 },
  apFeatTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 15, marginBottom: 3, color: stuInk },
  apFeatDesc: { fontSize: 12.5, lineHeight: 1.5, color: stuMute },
  apTimerRow: { display: "flex", alignItems: "center", gap: 10, padding: "12px 16px", background: stuAccentSoft, borderRadius: 8, marginBottom: 20 },
  apTimerIcon: { fontSize: 16 },
  apTimerTxt: { fontSize: 13, color: stuAccent, fontFamily: '"JetBrains Mono", ui-monospace, monospace', letterSpacing: 0.3 },
  apCtaRow: { marginTop: "auto", paddingTop: 16, borderTop: `1px solid ${stuRule}` },
  apCtaBtn: { display: "inline-block", background: stuInk, color: stuPaper, padding: "12px 24px", borderRadius: 8, fontSize: 14, fontFamily: "inherit" },

  /* Article detail (notes + reviews) */
  articleWrap: { maxWidth: 780, margin: "0 auto", padding: "40px 48px 100px" },
  articleHeader: { marginBottom: 48, paddingBottom: 32, borderBottom: `1px solid ${stuRule}` },
  articleH1: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 52, lineHeight: 1.06, letterSpacing: -1.4, margin: "16px 0 20px" },
  articleMeta: { display: "flex", alignItems: "center", gap: 10, fontSize: 13, color: stuMute, fontFamily: '"JetBrains Mono", ui-monospace, monospace', letterSpacing: 0.3, marginBottom: 20 },
  articleLede: { fontSize: 20, lineHeight: 1.6, color: "var(--splitItemColor)", fontStyle: "italic", margin: 0 },
  articleBody: {},
  articleH2: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 30, lineHeight: 1.15, letterSpacing: -0.5, margin: "48px 0 18px", color: stuInk },
  articleP: { fontSize: 17, lineHeight: 1.75, color: "var(--splitItemColor)", margin: "0 0 18px" },
  articleNav: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24, marginTop: 64, paddingTop: 32, borderTop: `1px solid ${stuRule}` },
  articleNavLink: { cursor: "pointer", textDecoration: "none" },
  articleNavDir: { display: "block", fontSize: 12, color: stuAccent, letterSpacing: 1, textTransform: "uppercase", marginBottom: 6 },
  articleNavTitle: { display: "block", fontFamily: '"Fraunces", Georgia, serif', fontSize: 20, lineHeight: 1.2, letterSpacing: -0.3, color: stuInk },

  /* Verdict box (reviews) */
  verdictBox: { marginTop: 48, padding: 32, background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 14 },
  verdictText: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 22, lineHeight: 1.4, letterSpacing: -0.3, margin: "12px 0 20px", color: stuInk },

  /* Tools coming soon */
  toolsComingSoon: { background: stuCard, border: `1px solid ${stuRule}`, borderRadius: 16, padding: 48, textAlign: "center", marginBottom: 32 },
  toolsCSIcon: { fontSize: 48, marginBottom: 16, opacity: 0.4 },
  toolsCSTitle: { fontFamily: '"Fraunces", Georgia, serif', fontWeight: 400, fontSize: 36, letterSpacing: -0.8, margin: "0 0 20px" },
  toolsCSBody: { fontSize: 16, lineHeight: 1.65, color: "var(--heroSubColor)", maxWidth: 640, margin: "0 auto 16px", textAlign: "center" },
  toolsCSList: { marginTop: 40, textAlign: "left", maxWidth: 600, margin: "40px auto 0" },
  toolsCSItems: { display: "flex", flexDirection: "column", gap: 20, marginTop: 18 },
  toolsCSItem: { display: "flex", gap: 16, alignItems: "flex-start" },
  toolsCSItemIcon: { width: 40, height: 40, background: stuAccentSoft, borderRadius: 10, display: "grid", placeItems: "center", fontSize: 18, flexShrink: 0 },
  toolsCSItemTitle: { fontFamily: '"Fraunces", Georgia, serif', fontSize: 18, marginBottom: 4 },
  toolsCSItemDesc: { fontSize: 14, lineHeight: 1.55, color: stuMute },
});

window.StudioV2 = StudioV2;
