summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/hugo.yml74
-rw-r--r--.gitignore2
-rw-r--r--config.toml11
-rw-r--r--content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md129
-rw-r--r--content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md169
-rw-r--r--content/txts/ciencia-politica-teoria-estado/_index.md22
-rw-r--r--content/txts/direito-constitucional/_index.md14
-rw-r--r--content/txts/linguagem-juridica/_index.md6
-rw-r--r--content/txts/metodologia/_index.md6
-rw-r--r--content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md6
-rw-r--r--content/txts/sociologia-antropologia-juridica/_index.md6
-rw-r--r--content/txts/sociologia-antropologia-juridica/divisao-trabalho.md3
-rw-r--r--content/txts/sociologia-antropologia-juridica/poder-dominacao.md3
-rw-r--r--content/txts/teoria-historia-direito/_index.md8
-rw-r--r--content/txts/teoria-historia-direito/constituicoes.md30
-rw-r--r--content/txts/teoria-historia-direito/direito-colonial.md8
-rw-r--r--content/txts/teoria-historia-direito/direito-contemporaneo.md5
-rw-r--r--content/txts/teoria-historia-direito/direito-moderno.md9
-rw-r--r--layouts/_default/baseof.html46
-rw-r--r--layouts/_default/list.html146
-rw-r--r--layouts/partials/disc-extras.html72
-rw-r--r--layouts/partials/quiz-runtime.html447
-rw-r--r--layouts/quizzes/list.html57
-rw-r--r--layouts/quizzes/single.html60
-rw-r--r--layouts/txts/single.html224
-rw-r--r--static/CNAME1
-rw-r--r--static/dark-mode.js59
-rw-r--r--static/dashboard.js130
-rw-r--r--static/footnotes.js77
-rw-r--r--static/home-filter.js72
-rw-r--r--static/keyboard-shortcuts.js73
-rw-r--r--static/main.css1560
-rw-r--r--static/manifest.json34
-rw-r--r--static/offline.html22
-rw-r--r--static/quiz-engine.js170
-rw-r--r--static/quiz-parser.js144
-rw-r--r--static/quiz-runtime.js92
-rw-r--r--static/sw.js68
-rw-r--r--static/toc.js52
39 files changed, 4117 insertions, 0 deletions
diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml
new file mode 100644
index 0000000..0966c4d
--- /dev/null
+++ b/.github/workflows/hugo.yml
@@ -0,0 +1,74 @@
+# Sample workflow for building and deploying a Hugo site to GitHub Pages
+name: Deploy Hugo site to Pages
+
+on:
+ # Runs on pushes targeting the default branch
+ push:
+ branches: ["main"]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+# Default to bash
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ # Build job
+ build:
+ runs-on: ubuntu-latest
+ env:
+ HUGO_VERSION: 0.128.0
+ steps:
+ - name: Install Hugo CLI
+ run: |
+ wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
+ && sudo dpkg -i ${{ runner.temp }}/hugo.deb
+ - name: Install Dart Sass
+ run: sudo snap install dart-sass
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - name: Setup Pages
+ id: pages
+ uses: actions/configure-pages@v5
+ - name: Install Node.js dependencies
+ run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
+ - name: Build with Hugo
+ env:
+ HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache
+ HUGO_ENVIRONMENT: production
+ run: |
+ hugo \
+ --minify \
+ --baseURL "${{ steps.pages.outputs.base_url }}/"
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./public
+
+ # Deployment job
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..17181ad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.hugo_build.lock
+/public
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..92d51fe
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,11 @@
+baseURL = "https://dir.sivaldodavi.com/"
+languageCode = "pt-br"
+title = "Direito - Sivaldo"
+
+[params]
+ description = "Estudos e materiais de Direito"
+
+[markup]
+ [markup.goldmark]
+ [markup.goldmark.renderer]
+ unsafe = true
diff --git a/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md b/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md
new file mode 100644
index 0000000..1321586
--- /dev/null
+++ b/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md
@@ -0,0 +1,129 @@
+---
+title: Exercício de aprendizado
+---
+
+? **Estudo de caso:** Durante o século XIX, o "Factory Act" na Inglaterra restringiu o trabalho infantil. O texto de Marx relata que, diante dessa proibição, os capitalistas ingleses, que antes consideravam o trabalho de crianças "seminuas" condizente com sua moral, passaram a investir em máquinas de quebrar pedras que já haviam sido inventadas pelos americanos. De acordo com o texto de Marx, o que determina a *introdução da maquinaria* no processo produtivo?
+- A preocupação ética do capitalista com o bem-estar e a saúde das mulheres e crianças trabalhadoras.
+- A necessidade de aumentar o tempo de lazer do operário, permitindo que ele se dedique a atividades culturais e religiosas.
+- A pressão das Instituições de Controle Social (ICS) morais, como a Igreja, que exigiam a modernização tecnológica das minas.
+- A escassez absoluta de trabalhadores, visto que o capital prefere sempre a máquina mesmo quando a mão de obra é gratuita.
+= O momento em que o custo da força de trabalho humana se torna superior ao custo de produção e manutenção da máquina.
+> Para Marx, a introdução da maquinaria no capitalismo não se dá por razões humanitárias, mas sim por uma lógica estrita de valor e lucro. A máquina substitui o trabalhador quando o seu uso barateia a produção.
+---
+
+? **Estudo de caso:** Um sindicato de operários organiza uma greve reivindicando que o valor excedente produzido pela jornada extra de trabalho seja convertido em salários. O Estado intervém enviando forças policiais para desocupar a fábrica e garantir a "ordem e o direito à propriedade". Relacionando a *Mais-Valia* com os conceitos de *ARE (Aparelho Repressivo de Estado)*, avalie as asserções: I. A mais-valia representa o trabalho não pago que é apropriado pelo capitalista, sendo a base da acumulação de capital. II. O Estado atua como um ARE (Aparelho Repressivo) quando utiliza a força física (Polícia) para proteger a estrutura que permite a extração da mais-valia. III. O conflito entre a busca pelo lucro (capital) e a sobrevivência do trabalhador (trabalho) exemplifica a Luta de Classes mencionada por Marx. É correto o que se afirma em:
+- I, apenas.
+- II, apenas.
+- I e II, apenas.
+- III, apenas.
+= I, II e III.
+> Todas as asserções estão corretas. A mais-valia é a exploração, o Estado age via ARE para garantir a ordem burguesa, e essa tensão evidencia a Luta de Classes.
+---
+
+? **Situação-problema:** Um designer gráfico trabalha em uma agência onde produz apenas logotipos para empresas que ele não conhece e cujos valores ele não compartilha. Ao final do dia, ele sente que seu trabalho não lhe pertence e que ele é apenas uma ferramenta para o enriquecimento do dono da agência. Este cenário ilustra a *Alienação* e a deturpação da Importância do Trabalho. Assinale a alternativa que descreve essa relação:
+- A alienação é um processo positivo, pois permite que o trabalhador não se preocupe com as consequências éticas do que produz.
+- A importância do trabalho no capitalismo reside na capacidade do operário de se tornar dono dos meios de produção através da economia do seu salário.
+- O designer não é alienado, pois utiliza um computador (maquinaria), o que, segundo Marx, devolve ao homem o controle total sobre a criação.
+- A alienação ocorre porque o trabalhador se recusa a cooperar com seus colegas, preferindo o isolamento da produção artesanal.
+= O trabalho, que deveria ser a forma do homem se realizar e transformar o mundo, torna-se um fardo onde o trabalhador se sente um "miserável" (wretch).
+> A alienação ocorre quando o trabalhador não se reconhece no produto, não controla o processo e vê sua atividade como mera obrigação para sobrevivência.
+---
+
+? **Estudo de caso:** Imagine um canteiro de obras onde 20 homens precisam levantar uma viga pesada. Se trabalharem isoladamente, nenhum conseguirá movê-la. Se trabalharem juntos, o esforço é realizado em segundos. Marx utiliza exemplos similares para explicar a "potência de massa". Sobre a *cooperação simples* descrita por Marx, assinale a alternativa correta:
+- A cooperação não altera a produtividade, apenas facilita o convívio social entre os trabalhadores.
+- A cooperação é uma invenção da era da tecnologia digital, inexistente no período da manufatura.
+- Na cooperação simples, cada operário é dono de sua própria ferramenta e decide o ritmo de sua produção.
+- A reunião de operários em um mesmo local aumenta os custos de produção, pois exige mais espaço físico.
+= O esforço coletivo gera uma força superior à soma das forças individuais isoladas, reduzindo o tempo de trabalho necessário.
+> O conceito de "potência de massa" explica que a cooperação não é só a soma de indivíduos, mas a criação de uma força coletiva mais eficiente.
+---
+
+? **Estudo de caso:** Uma grande montadora de veículos em Minas Gerais organiza a sua produção de forma que cada operário aperte apenas um parafuso específico durante toda a jornada. Simultaneamente, essa fábrica faz parte de uma rede global que depende de mineradoras de ferro e empresas de logística. Marx diferencia esses dois níveis de especialização para explicar a estrutura do capital. Sobre a *Divisão do Trabalho*, assinale a alternativa correta:
+- A divisão técnica ocorre entre os diferentes ramos da indústria (como agricultura e indústria), enquanto a social ocorre dentro da oficina.
+- Na divisão técnica da manufatura, o operário mantém a sua autonomia e o domínio completo sobre o produto final da sua atividade.
+- Marx defende que a divisão técnica do trabalho é uma ferramenta de libertação, pois reduz o esforço intelectual exigido do trabalhador.
+- O Estado, como ICS soberana, proíbe a divisão técnica do trabalho para garantir que todos os cidadãos possuam múltiplas habilidades profissionais.
+= A divisão social do trabalho fragmenta a sociedade em profissões, enquanto a divisão técnica fragmenta o próprio operário em uma peça parcial da máquina.
+> A divisão social separa a sociedade em ramos; a técnica ocorre 'da porta para dentro' da fábrica, fragmentando o processo produtivo.
+---
+
+? **Situação-problema:** Em uma metrópole, observamos uma clara separação entre os bairros onde residem os engenheiros, juízes e médicos, e as periferias onde moram os operários da construção civil e auxiliares de limpeza. Marx explica que essa organização não é apenas funcional, mas reflete a estrutura de classes e a distribuição desigual da riqueza produzida. Sobre a *Divisão Social do Trabalho*, assinale a alternativa correta:
+- Ela garante que todos os indivíduos, independentemente da profissão, recebam exatamente o mesmo valor pela sua contribuição social.
+- É uma escolha individual baseada puramente no talento, sem qualquer relação com o acesso desigual à educação ou aos meios de produção.
+- O Estado-Jurídico atua para eliminar a divisão social do trabalho, transformando todos os cidadãos em "operários parciais" vitalícios.
+- Na manufatura, a divisão social é o que permite que o operário fabrique uma mercadoria completa sem precisar de outros colegas.
+= Diferencia-se da divisão técnica pois organiza os diversos ramos da produção na sociedade, consolidando a hierarquia entre quem detém o capital e quem vende a força de trabalho.
+> A divisão social no capitalismo reflete a estrutura de classes, distribuindo de forma desigual a riqueza e os espaços sociais.
+---
+
+? **Estudo de caso:** Na manufatura, o processo de produção é decomposto em várias operações parciais. O operário, antes um artesão que conhecia todo o processo, passa a realizar apenas um movimento repetitivo, tornando-se um "operário parcial" vinculado a uma função vitalícia. Este processo de *alienação no ambiente de trabalho* resulta em qual consequência para o trabalhador, segundo a lógica marxista?
+- O aumento da criatividade e da liberdade, pois o operário tem menos responsabilidades sobre o produto final.
+- A valorização do saber técnico do artesão, que passa a comandar as máquinas com total autonomia e poder de decisão.
+- A eliminação da hierarquia na fábrica, visto que todos os operários parciais possuem o mesmo nível de conhecimento total.
+- O fortalecimento do vínculo emocional entre o operário e a mercadoria, já que ele a produz inteiramente com suas mãos.
+= A transformação do operário em uma peça de um mecanismo vivo, cuja habilidade se torna um detalhe automático e limitado.
+> Este é o clímax da degradação na manufatura: o operário perde autonomia e vira engrenagem viva de um processo que não domina.
+---
+
+? **Situação-problema:** Durante a tramitação de uma nova lei ambiental, grandes grupos empresariais do setor industrial (a burguesia industrial) financiam institutos de pesquisa e contratam escritórios de advocacia para pressionar parlamentares. O objetivo é garantir que a norma não encareça o custo de produção, mesmo que isso aumente a poluição. De acordo com a *Crítica de Marx ao Estado e o papel dos Lobbies*, avalie as asserções: I. O Estado moderno tende a funcionar como um comitê que administra os negócios comuns da burguesia. II. O Direito é visto como uma superestrutura que reflete as relações de poder econômico. III. O lobby burguês é uma forma de "Ação Indireta" que garante que o Estado-Jurídico mantenha a lucratividade. É correto o que se afirma em:
+- I e II, apenas.
+- II e III, apenas.
+- I, apenas.
+- III, apenas.
+= I, II e III.
+> Todas corretas. O Estado atua como gestor dos negócios burgueses, o Direito reflete a infraestrutura econômica e os lobbies são ferramentas práticas desse poder.
+---
+
+? **Estudo de caso:** Marx observa que a produção capitalista não nasce de uma mudança tecnológica súbita, mas de uma mudança na organização do trabalho. Antes das máquinas, o que definia o início do capitalismo era a reunião de muitos trabalhadores em uma grande oficina, sob o comando de um único patrão, realizando as mesmas tarefas que antes faziam de forma isolada. De acordo com o texto, qual é o *ponto de partida histórico e formal* da produção capitalista?
+- A invenção da máquina a vapor e a substituição completa do trabalho manual por motores.
+- A distribuição de lucros igualitária entre o mestre artesão e seus aprendizes na oficina corporativa.
+- A proibição do trabalho manual em favor de um sistema baseado exclusivamente no comércio de importação.
+- A redução quantitativa de operários para garantir que cada um produza uma mercadoria completa do início ao fim.
+= O momento em que o capitalista ocupa simultaneamente um grande número de operários no mesmo campo de trabalho.
+> O capitalismo nasce formalmente com a cooperação simples: a reunião de muitos trabalhadores artesanais no mesmo galpão sob o comando do capital.
+---
+
+? **Situação-problema:** Um escritório de advocacia em expansão decide alterar sua forma de trabalho. Antes, cada advogado cuidava de um processo do início ao fim. Agora, o titular reuniu 20 advogados em um mesmo salão e determinou que cinco farão apenas pesquisas jurisprudenciais, dez redigirão apenas petições e cinco cuidarão exclusivamente de prazos processuais. De acordo com o texto de Marx sobre a *Manufatura*, essa mudança organizacional caracteriza:
+- A manutenção do sistema de artesanato corporativo, onde a habilidade individual permanece sendo o fator determinante da qualidade jurídica.
+- Uma revolução tecnológica, pois a reunião de pessoas no mesmo local é o que Marx define como a invenção da maquinaria pesada.
+- A eliminação da autoridade do capitalista, visto que o trabalho coletivo permite que os advogados decidam autonomamente.
+- O fim da divisão social do trabalho, uma vez que todos os ramos da sociedade passam a ser representados dentro daquela única oficina.
+= A criação de um "operário parcial", onde o advogado perde a visão do conjunto do processo para ganhar agilidade técnica em uma tarefa específica e repetitiva.
+> O estudo de caso aplica a divisão técnica à advocacia: o profissional deixa de conhecer o todo e repete apenas uma etapa processual específica.
+---
+
+? **Situação-problema:** Em um mutirão de assistência judiciária gratuita, 50 advogados trabalham simultaneamente em um ginásio para atender uma comunidade. O coordenador do evento observa que o grupo consegue atender dez vezes mais pessoas do que se cada advogado trabalhasse isoladamente. Sobre o conceito de *Cooperação* em Marx, como se explica esse fenômeno?
+- A cooperação reduz a produtividade, pois o "operário médio" tende a trabalhar mais devagar quando está cercado de colegas.
+- O aumento da produção deve-se exclusivamente ao fato de que, em grupo, os advogados deixam de ser "miseráveis" (wretches) e tornam-se capitalistas.
+- A reunião de muitos trabalhadores no mesmo lugar elimina a diferença quantitativa entre a oficina do mestre e a grande indústria.
+- Marx defende que a cooperação só ocorre quando há o uso de cavalos ou mulheres puxando barcos, não sendo aplicável ao trabalho intelectual.
+= O trabalho coletivo cria uma força produtiva nova (potência de massa), que é essencialmente gratuita para quem coordena e detém os meios de produção.
+> A força extra gerada pela coletividade (potência de massa) não é remunerada individualmente; ela é apropriada de forma gratuita pelo coordenador/capitalista.
+---
+
+? **Situação-problema:** Uma plataforma de tecnologia aumenta a taxa cobrada dos motoristas parceiros, alegando necessidade de lucro para os acionistas. Os motoristas organizam uma paralisação nacional exigindo melhores condições e direitos trabalhistas. O conflito descrito acima é uma manifestação contemporânea da *Luta de Classes*. Como Marx define a base desse conflito?
+- É uma disputa estética entre diferentes grupos de consumidores por marcas de luxo.
+- Trata-se de um mal-entendido técnico que pode ser resolvido com uma melhor "gestão de planilhas" pela tecnocracia estatal.
+- A luta de classes só existe quando há violência armada física, não se aplicando a greves ou reivindicações jurídicas pacíficas.
+- O conflito ocorre porque os motoristas desejam ser ARE (Aparelhos Repressivos) e o Estado não permite.
+= É o choque irreconciliável entre as Relações de Produção (quem possui o capital/app) e as forças produtivas que detêm apenas a força de trabalho.
+> A greve de aplicativos ilustra o antagonismo estrutural: os detentores do capital focam no lucro, enquanto as forças produtivas buscam sobrevivência.
+---
+
+? **Estudo de caso:** Em um escritório de contabilidade tecnocrático, os funcionários são monitorados por softwares que registram cada segundo de inatividade. O trabalhador sente-se como um estranho em relação ao relatório final que produz, não compreendendo a finalidade do seu esforço para a sociedade, sentindo-se apenas uma engrenagem na "Máquina Fria" do Estado-Burocrático. O cenário acima descreve o encontro dos conceitos de *Alienação e Microfísica do Poder*. Assinale a alternativa que explica essa relação:
+- A alienação é eliminada pela microfísica do poder, pois a vigilância constante faz o trabalhador sentir-se parte importante da empresa.
+- O poder, segundo Foucault, reside apenas no topo da hierarquia, o que impede que o trabalhador se sinta alienado nas bases.
+- A burocracia estatal brasileira resolve o problema da alienação ao transformar a gestão de pessoas em um processo puramente afetivo.
+- A consciência de classe surge automaticamente quando o trabalhador é submetido à vigilância técnica dos softwares de produtividade.
+= O estranhamento do trabalhador (alienação) é reforçado por mecanismos invisíveis de controle e vigilância (microfísica) que operam no cotidiano.
+> A união entre Marx (estranhamento do produto) e Foucault (vigilância tecnológica dos corpos) descreve o controle tecnocrático moderno.
+---
+
+? Assinale a alternativa que descreve a *transição da manufatura para a grande indústria*, conforme discutido no texto:
+- A grande indústria elimina a necessidade de capital, tornando o trabalhador dono dos meios de produção.
+- A manufatura é mais eficiente que a grande indústria, pois utiliza ferramentas mais simples e baratas.
+- A transição para a indústria ocorreu sem conflitos, pois os operários preferiam a monotonia das máquinas.
+- O uso de máquinas na Inglaterra começou exclusivamente para proteger mulheres e crianças de trabalhos pesados.
+= A manufatura baseia-se na habilidade manual do operário, enquanto a grande indústria baseia-se no sistema de máquinas que executa as tarefas antes manuais.
+> Na manufatura o trabalho depende da destreza manual. Na maquinofatura, a máquina é a força motriz e o trabalhador apenas a serve.
+---
diff --git a/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md b/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md
new file mode 100644
index 0000000..c4ea725
--- /dev/null
+++ b/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md
@@ -0,0 +1,169 @@
+---
+title: Exercício de aprendizado
+---
+
+? No cenário brasileiro, críticas recorrentes apontam que a gestão pública é frequentemente entregue a técnicos que priorizam métricas orçamentárias em detrimento das necessidades humanas. Essa "gestão de planilhas" gera um distanciamento entre a administração e os anseios democráticos da população. Sobre o conceito de *Tecnocracia e Burocracia*, assinale a alternativa que apresenta o problema central descrito no material:
+- A tecnocracia fortalece a democracia ao permitir que o poder econômico assuma o controle político direto diariamente.
+- A burocracia estatal é uma invenção das sociedades pré-capitalistas que visava humanizar as relações de poder.
+- No Brasil, o poder político e o poder econômico são polos que nunca se encontram, o que elimina qualquer risco de autoritarismo.
+- A tecnocracia elimina as formas de resistência, pois oferece soluções científicas que satisfazem todos os estratos sociais.
+= O domínio dos tecnocratas resulta em uma "máquina fria", onde a tensão entre a gestão técnica e a anomia social se torna permanente.
+> A tecnocracia transforma o Estado em uma 'máquina fria' governada por especialistas técnicos e métricas, gerando tensão com as necessidades humanas reais (anomia social) e afastando-se do ideal democrático participativo.
+---
+
+? Em uma análise sobre a sociabilidade brasileira, um pesquisador observa que o Estado atua como o ápice soberano de um mecanismo ideológico que abrange toda a sociedade. Ele nota que, mesmo em instituições privadas como a família e a igreja, as diretrizes de comportamento são indiretamente moldadas pelo aparato estatal. Considerando o conceito de *Estado como ICS Soberana*, assinale a alternativa correta:
+- Nas sociedades modernas, o Estado atua de forma isolada, sem interpenetração nas demais Instituições de Controle Social (ICS).
+- A sociabilização primária, realizada pela família, é um processo puramente acidental e livre de qualquer influência das ICS.
+- O controle social pelo Direito é exercido apenas através da força física, sendo a moral e a ética domínios exclusivos da Igreja.
+- O Estado abdica de sua soberania nas sociedades complexas, transferindo o controle social integralmente para as corporações privadas.
+= O Estado é o aparelho técnico-ideológico supremo, capaz de coagir organismos sociais tanto por ação direta quanto por infiltração invisível.
+> O Estado moderno não atua apenas de forma coercitiva direta, mas também permeia as instituições privadas (família, igreja) ditando regras morais, jurídicas e ideológicas, agindo como a ICS soberana coordenadora.
+---
+
+? Em uma determinada comunidade urbana, observa-se que as regras de convivência e a resolução de conflitos não derivam apenas do Código Civil, mas de normas morais religiosas e diretrizes pedagógicas das escolas locais. Contudo, em situações de crise, o Estado intervém através de decretos que regulam desde o currículo escolar até a estrutura familiar permitida para benefícios sociais. Considerando a *Arquitetura da Sociabilização* e a atuação do Estado como ICS Soberana, avalie as asserções a seguir: I. O Estado atua de forma direta por meio do braço forte (Polícia e Judiciário) e de forma indireta ao ditar regras que interpenetram instituições privadas como a família e a escola. II. Nas sociedades complexas, a família perdeu sua função de base primária de sociabilização, sendo integralmente substituída pelo aparelho técnico-ideológico estatal. III. O sistema Judiciário e Penal é o ponto de convergência absoluto, pois acumula funções de coerção ideológica (pelo pensamento) e repressiva (pela força física). É correto o que se afirma em:
+- I e II, apenas.
+- II e III, apenas.
+- I, II e III.
+- I, apenas.
+= I e III, apenas.
+> A assertiva II é falsa porque a família não foi integralmente substituída; ela continua sendo a base primária, embora sob forte influência estatal. As assertivas I e III refletem perfeitamente as vias de ação do Estado e a dupla função do sistema penal.
+---
+
+? A sociabilização de um indivíduo e as suas estratégias de sobrevivência dentro de uma comunidade não ocorrem de forma aleatória. Elas são coordenadas por um conjunto de instituições que tecem um mecanismo técnico-científico e ideológico abrangendo toda a estrutura social. Com base no conceito de *Instituições de Controle Social (ICS)*, assinale a alternativa correta:
+- As ICS atuam de forma isolada e independente, sem formar um bloco de controle sobre o comportamento do indivíduo.
+- As instituições de controle social possuem uma atuação meramente biológica, sem qualquer carga ideológica ou científica.
+- A escola atua como uma ICS focada exclusivamente no controle moral, deixando a disciplina do saber para a igreja.
+- O controle social exercido pelas ICS é opcional, permitindo que o indivíduo ignore as pressões das instituições básicas.
+= A família é considerada a base primária das ICS, enquanto o Estado representa o ápice soberano desse mecanismo.
+> Na sociologia do controle, a Família representa o núcleo/base da sociabilização, enquanto o Estado-Jurídico opera no topo da pirâmide como a instituição que rege, centraliza e coordena todo o sistema de controle.
+---
+
+? A centralidade sociológica das instituições mudou ao longo da história para atender às demandas de cada época. O que antes era baseado no sangue e na fé, passou a focar na produção industrial e, posteriormente, na burocracia do saber e da lei formal. Considerando a *Evolução da Centralidade Sociológica*, relacione os períodos históricos com seus respectivos binômios:
+- No pré-capitalismo, a centralidade residia no binômio Escola-Jurídico, fundamentada na burocracia técnica.
+- Na pós-modernidade, o binômio Família-Igreja retorna como o eixo central de sociabilização e controle formal.
+- O binômio Escola-Jurídico caracteriza o período pré-capitalista, onde a lei formal era a única unidade de controle.
+- A centralidade baseada na fé e no sangue (Família-Igreja) é o modelo predominante na sociedade pós-moderna atual.
+= O capitalismo industrial estabeleceu o binômio Família-Escola para preparar corpos e mentes para a produção em massa.
+> A Revolução Industrial demandou um novo perfil de trabalhador. O binômio Família-Escola substituiu a hegemonia da Igreja ao assumir a função de disciplinar e capacitar técnica e fisicamente os indivíduos para o trabalho fabril.
+---
+
+? Dois teóricos analisam como o Estado-Jurídico se relaciona com a base social (Família, Escola, Sindicatos). Um deles defende que o Estado atua de cima para baixo, determinando a base; o outro sugere que as instituições se sobrepõem no espaço social, gerando lacunas de autonomia. Comparando os modelos de *Bourdieu e Althusser* presentes no material, assinale a alternativa correta:
+- Bourdieu defende que o Estado-Jurídico atua de forma vertical e absoluta, eliminando qualquer lacuna para a autonomia.
+- Para Althusser, as instituições se sobrepõem de modo que sempre surgem espaços de resistência onde as esferas não se tocam.
+- Ambos os autores concordam que a determinação do Estado sobre a base social é elástica e sempre permite a contestação.
+- O modelo de Althusser foca na interpenetração espacial, enquanto Bourdieu foca na determinação hierárquica do Estado-Jurídico.
+= O modelo de interpenetração espacial de Bourdieu prevê que, onde as circunferências não se tocam, surgem espaços de resistência.
+> Enquanto Althusser foca em uma visão estrutural top-down (determinação vertical pelo Estado), a leitura de Bourdieu foca nos campos (esferas) que se sobrepõem imperfeitamente, criando espaços sociais livres ou de resistência.
+---
+
+? Hannah Arendt defende que a verdadeira força política não nasce da violência bruta, mas da capacidade de agir em conjunto através da palavra e do convencimento. Em contrapartida, sistemas penais modernos debatem-se entre o castigo puramente vingativo e a restauração dos laços sociais. Avalie as alternativas sobre os *Paradigmas de Punição e a Potência da Não Violência*:
+- O Direito Repressivo foca na reintegração do infrator, enquanto a Justiça Restaurativa foca no suplício corporal.
+- A não violência, como demonstrado por Gandhi, é uma ferramenta passiva que aceita o poder mecânico do Estado sem resistência.
+- A apatia do cidadão no espaço público é vista por Arendt como uma forma saudável de preservar a individualidade contra o Estado.
+- A responsabilidade coletiva, para Arendt, significa que o cidadão não é cúmplice das atrocidades produzidas pelo Estado em democracias.
+= O Direito Repressivo foca na vingança e exclusão social, enquanto a Justiça Restaurativa foca nas causas do crime e na reativação social.
+> O modelo punitivo (Direito Repressivo) baseia-se na retribuição, dor e isolamento do infrator. Já a Justiça Restaurativa propõe reatar o tecido social, tratando as origens sociológicas do conflito.
+---
+
+? Hannah Arendt alerta que a maior ameaça à liberdade não é necessariamente o golpe de força, mas a apatia do cidadão que permite a ascensão de poderes despóticos através de meios legais e democráticos, como ocorreu em regimes totalitários do século XX. Sobre a *Microfísica do Poder e a Potência da Não Violência*, analise as alternativas:
+- O poder, segundo Foucault, reside exclusivamente em edifícios governamentais centrais, não se estendendo a hospitais ou escolas.
+- A desobediência pacífica e a ocupação ética dos espaços públicos são ferramentas inúteis contra o poder mecânico e unidimensional do Estado.
+- A justiça restaurativa foca na punição sumária e no suplício corporal como forma de reativar o tecido social e prevenir o crime.
+- A impunidade do Estado oficial é a única causa da violência moderna, visto que os microaparelhos não possuem poder punitivo autônomo.
+= O poder despótico e o totalitarismo podem emergir utilizando mecanismos democráticos e o apoio das massas, alimentados pela inércia no espaço público.
+> Arendt demonstra que o mal, muitas vezes, é 'banal' e burocrático. O totalitarismo triunfa quando os cidadãos perdem a potência de agir em conjunto (espaço público), permitindo que tiranos usem as leis e o sistema a seu favor.
+---
+
+? Um projeto de lei propõe que, em vez de apenas encarcerar indivíduos, o sistema penal deve focar na reparação do dano à vítima e na reintegração do infrator, buscando entender as causas originárias do crime para evitar a reincidência. Considerando o comparativo entre o *Direito Repressivo e a Justiça Restaurativa*, avalie as asserções:
+I. O Direito Repressivo possui foco central na vítima, utilizando muitas vezes a mediação para curar o suplício corporificado.
+II. A Justiça Restaurativa busca a reativação social por meio da prevenção e da reintegração, focando nas causas do conflito.
+III. A violência moderna é causada exclusivamente pela impunidade estatal, já que o poder punitivo não existe fora do Estado oficial. É correto o que se afirma em:
+- I, apenas.
+- I e II, apenas.
+- II e III, apenas.
+- I, II e III.
+= II, apenas.
+> A assertiva I está incorreta porque o Direito Repressivo clássico 'rouba' o conflito da vítima (teoria de Nils Christie) focando exclusivamente na punição estatal sobre o ofensor. A III é incorreta porque a microfísica de Foucault prova que o poder punitivo existe além do Estado.
+---
+
+? A análise histórica de regimes como o de Hitler e Stalin revela que o poder totalitário não necessita apenas de armas, mas também da arregimentação de massas e, principalmente, do silêncio daqueles que se abstêm da vida política. Com base nas reflexões de *Hannah Arendt* apresentadas no material, é correto afirmar que:
+- A maior ameaça à liberdade é a força física do ditador, sendo a participação cidadã irrelevante para conter o despotismo.
+- O poder despótico surge exclusivamente de golpes militares violentos, nunca utilizando mecanismos democráticos ou votos populares.
+- A independência da Índia liderada por Gandhi exemplifica como a força bruta é o único caminho para desmontar um império.
+- O espaço público mantém seu sentido original mesmo quando os indivíduos deixam de participar ou de dar significado a ele.
+= A inércia no espaço público e a apatia do cidadão eliminam o contrapeso necessário ao poder pelo poder do Estado.
+> Para Arendt, o poder pertence ao grupo e só existe enquanto o grupo atua conjuntamente. A apatia e o recolhimento ao espaço puramente privado destroem a potência política que freia os abusos do Estado.
+---
+
+? Durante uma reforma no sistema de segurança pública, o governo decide investir simultaneamente em novas viaturas e armamentos para a Polícia Militar e em campanhas publicitárias que exaltam os "valores da família e do civismo" nas escolas públicas. Relacione as ações do governo com os conceitos de *AIE (Aparelhos Ideológicos de Estado)* e *ARE (Aparelhos Repressivos de Estado)*:
+- O investimento em armamento policial exemplifica um AIE, pois foca na persuasão e na ética do cidadão.
+- As campanhas sobre civismo nas escolas são exemplos de ARE, pois utilizam a hostilidade para garantir a ordem.
+- O sistema judiciário atua exclusivamente como um ARE, sendo incapaz de exercer qualquer função ideológica na sociedade.
+- Segundo Weber, a "coação jurídica" é um mecanismo exclusivo dos aparelhos repressivos, não possuindo carga ideológica.
+= A Polícia e o Exército operam a coercitividade pela violência física (ARE), enquanto a Escola e a Família operam pela ideologia e moral (AIE).
+> Na teoria althusseriana, os AREs baseiam-se primordialmente na força e repressão física (polícia), enquanto os AIEs funcionam moldando a subjetividade através da moral, conhecimento e ideologia (escolas, mídia).
+---
+
+? Em uma sociedade complexa, o Estado exerce sua centralidade através de um aparelho técnico-ideológico supremo. Para manter a ordem, ele pode utilizar tanto operações ostensivas de segurança quanto a criação de regulamentações que interferem no funcionamento de entidades privadas. Sobre as formas de *Ação Direta e Indireta* do Estado como ICS, é correto afirmar que:
+- A ação direta ocorre quando o Estado se infiltra de forma invisível nas famílias para ditar regras de convivência interna.
+- A ação indireta caracteriza-se pelo uso do "Braço Forte", como o emprego do Exército e da Polícia em operações de rua.
+- A interpenetração nas ICS privadas é uma forma de ação direta, pois o Estado utiliza a força física para coagir famílias.
+- Nas sociedades modernas, o Estado abandonou a ação direta, limitando-se a influenciar o pensamento através da mídia.
+= O Estado exerce ação direta via Judiciário e órgãos públicos, e ação indireta ao ditar regras para escolas e corporações.
+> A ação direta se dá pelas próprias 'mãos' do Estado (seus tribunais, sua polícia, seu fisco). A ação indireta ocorre por meio de leis e normas que forçam instituições não estatais a se adaptarem às vontades do Estado.
+---
+
+? Ao analisar a relação entre Estado, Escola e Família sob a ótica de Bourdieu e Althusser, observa-se que, embora o Estado tente determinar a base social de cima para baixo, existem lacunas onde a norma estatal não alcança plenamente a conduta dos indivíduos. Sobre os *Modelos de Interpenetração das ICS*, é correto afirmar que:
+- A teoria de Althusser prevê que as instituições se sobrepõem de forma elástica, permitindo sempre a autonomia total do indivíduo.
+- Os "Espaços de Resistência" surgem precisamente nos pontos de interpenetração espacial onde as circunferências das instituições se sobrepõem perfeitamente.
+- A determinação do Estado-Jurídico sobre a base (família e sindicatos) elimina qualquer possibilidade de conduta contestatória ou autonomia.
+- No modelo de Bourdieu, o Estado é a única instituição capaz de gerar sociabilização, sendo as demais meros apêndices sem interpenetração.
+= Onde as esferas de controle das instituições não se tocam, abrem-se fronteiras para a autonomia e o surgimento de espaços de resistência.
+> Na metáfora dos espaços sociais, como as instituições (família, religião, estado) não conseguem estar em todos os lugares ao mesmo tempo, suas imperfeições de contato geram vácuos sociológicos onde floresce a autonomia e a resistência.
+---
+
+? Apesar da dominância do Estado e das instituições de controle, os teóricos observam que o sistema não é onipresente. Existem "bolsões de autonomia" que sobrevivem às pressões institucionais, abrindo fronteiras para condutas contestatórias. Sobre o surgimento desses *Espaços de Resistência*, assinale a alternativa correta:
+- Eles surgem exclusivamente através da violência física contra os aparelhos repressivos de Estado (ARE).
+- A resistência é impossível em sociedades pós-modernas devido à total eficiência da burocracia do saber.
+- A microfísica do poder demonstra que a resistência só existe se for autorizada pelas instituições de controle moral.
+- A autonomia do indivíduo é determinada pelo Estado-Jurídico, que define onde e como a resistência pode ocorrer.
+= Os espaços de resistência emergem nas lacunas deixadas pela falha na interpenetração absoluta das instituições.
+> O controle social não é hermético. Onde o maquinário repressivo e ideológico falha em preencher a vida do sujeito, abrem-se os 'bolsões' onde novas práticas, contestações e ressignificações podem ocorrer.
+---
+
+? A Sociologia do Controle observa que a centralidade das Instituições de Controle Social (ICS) não é estática, deslocando-se para acompanhar as transformações tecnológicas e burocráticas da sociedade. Se o capitalismo industrial foi marcado pelo binômio Família-Escola, a contemporaneidade é definida pela "Máquina Fria" da tecnocracia e pela onipresença dos fluxos informacionais. Nesse contexto, a unidade predominante de controle deixa de ser apenas a disciplina do saber e passa a integrar a gestão da visibilidade e do discurso. Com base na *Evolução da Centralidade Sociológica*, qual binômio melhor define a configuração atual das estratégias de poder e controle social?
+- Família e Igreja, baseando-se no eixo central do sangue e da fé para a manutenção da ordem.
+- Família e Escola, preparando mentes e corpos para a produção em massa e o trabalho industrial.
+- Escola e Sindicatos, focando na organização política da base trabalhadora contra o ápice soberano.
+- Igreja e Comunicação, utilizando o controle moral para resistir à interpenetração do Estado-Jurídico.
+= Estado/Jurídico e Comunicação, consolidando a burocracia da lei formal e o controle dos fluxos de informação.
+> Na pós-modernidade, a força coordenadora migra pesadamente para o aparato burocrático-legal do Estado aliado ao controle das massas pela mídia e plataformas tecnológicas (Comunicação).
+---
+
+? No século XXI, um cidadão percebe que a maioria de seus conflitos cotidianos não é resolvida por conselhos familiares ou autoridades religiosas, mas por burocracias digitais, tribunais e normas técnicas geridas por especialistas que ele nunca conheceu. Considerando a *Evolução da Centralidade do poder* em diversos períodos da história humana e a *Tecnocracia*, assinale a alternativa correta:
+- O capitalismo industrial manteve a fé e o sangue (binômio Família-Igreja) como os eixos centrais de preparação de mentes para a produção.
+- A tecnocracia estatal aproxima a gestão pública dos valores humanos e da democracia participativa, eliminando o risco da anomia.
+- A separação entre o poder econômico e o aparelho de Estado em regimes autoritários é o que garante a liberdade e os espaços de contestação.
+- A "Máquina Fria" da burocracia estatal impede o surgimento de novas formas de resistência, pois transforma a gestão de pessoas em gestão de planilhas.
+= Na pós-modernidade, o binômio Escola-Jurídico torna-se a unidade predominante de controle, consolidando a lei formal e a burocracia do saber.
+> No mundo contemporâneo, a autoridade não deriva mais prioritariamente do sagrado (Igreja), mas sim do conhecimento técnico especializado, das certificações (Escola/Saber) e da normalização das leis (Jurídico).
+---
+
+? Um sociólogo analisa o funcionamento de uma unidade de internação de menores. Ele percebe que, ao mesmo tempo que os jovens são submetidos a uma disciplina rígida e vigilância armada (coação física), eles participam de oficinas de ética e moralidade que visam moldar seu comportamento futuro (coação jurídica/ideológica). Com base nos conceitos de *Aparelhos Ideológicos (AIE) e Aparelhos Repressivos de Estado (ARE)*, assinale a alternativa correta:
+- A escola e a igreja são exemplos típicos de ARE, pois utilizam a violência física para garantir a manutenção da ordem social.
+- A coação jurídica, conforme o conceito de Weber, baseia-se exclusivamente na hostilidade física exercida pelo Exército.
+- Os AIEs são operados unicamente pelo braço forte do Estado, sem qualquer interpenetração em instituições da esfera privada.
+- A microfísica do poder demonstra que a repressão física é a única ferramenta eficaz de controle em sociedades pós-modernas.
+= O Estado exerce sua soberania de forma bipolar, alternando entre a persuasão ideológica e a repressão física, sendo o sistema penal o local onde ambas se fundem.
+> O sistema penal/prisional é o maior exemplo prático de convergência no pensamento estruturalista de Althusser. Ele atua duramente como ARE (muros, força letal), mas possui um fortíssimo elemento de AIE (tentativa disciplinar e ideológica de 'reeducar' o interno).
+---
+
+? Michel Foucault propõe que o poder não deve ser visto apenas como uma estrutura centralizada (como um palácio ou sede de governo), mas como algo que se manifesta nos hospitais, nas fábricas e nos escritórios locais, muitas vezes à revelia da lei estatal. Com base na *Microfísica do Poder*, é correto afirmar que:
+- O poder é uma propriedade que pertence exclusivamente ao Estado e que é distribuída de cima para baixo de forma linear.
+- O poder de Foucault reside no acordo tácito onde o Estado abre mão de vigiar as classes mais pobres e excluídas.
+- A vigilância especializada exercida por subórgãos locais é sempre menos eficaz que o ordenamento jurídico formal.
+- A resistência ao poder só é possível quando se ataca o edifício central do Estado, ignorando as instituições locais.
+= O poder é imanente e descentralizado, o que significa que cada micro-organização inventa sua própria repressão e ideologia.
+> Foucault quebra o paradigma de que o poder só emana do Estado (visão macro). Ele argumenta que o poder é uma malha capilar (micro) presente no panoptismo dos hospitais, manicômios e indústrias, operando uma vigilância disciplinar constante.
+---
+
diff --git a/content/txts/ciencia-politica-teoria-estado/_index.md b/content/txts/ciencia-politica-teoria-estado/_index.md
new file mode 100644
index 0000000..ab0b6bb
--- /dev/null
+++ b/content/txts/ciencia-politica-teoria-estado/_index.md
@@ -0,0 +1,22 @@
+---
+title: "Ciência Política e Teoria do Estado"
+files:
+ - name: "Teoria Geral do Estado e Ciência Política"
+ url: "https://drive.google.com/file/d/1_mkWxoowmvMUq6yZKzfs-pbV1l9cM7Jl/view?usp=drive_link"
+ - name: "Natureza Humana"
+ url: "https://drive.google.com/file/d/1vLFlqSL4PjBBmZvuBM2HOH8--q8cUKt6/view?usp=drive_link"
+ - name: "Sociedade"
+ url: "https://drive.google.com/file/d/1S1aRtnCBWjrVqGNB-lIJU-n0PnsZRNhw/view?usp=drive_link"
+ - name: "Estado"
+ url: "https://drive.google.com/file/d/1Q5fGUrHiPfo9wN4llh_bwggBLrr2W6vQ/view?usp=drive_link"
+ - name: "Formas de Governo"
+ url: "https://drive.google.com/file/d/1UEQGWODoYjjHt-3Dae3-iczadLkfOFks/view?usp=drive_link"
+ - name: "Democracia"
+ url: "https://drive.google.com/file/d/1L6vlkRtrau2-Z0TtWA3uVvbrPZ2BDiK8/view?usp=drive_link"
+ - name: "Sistemas de Governo"
+ url: "https://drive.google.com/file/d/1FaJBl6GVfuARcjOGKzQuyyRdR7CzREcu/view?usp=drive_link"
+ - name: "O Estado Brasileiro"
+ url: "https://drive.google.com/file/d/1kf1oAGpA0YK3gXjGplPlANoXg8kxaHxD/view?usp=drive_link"
+ - name: "Corrupção"
+ url: "https://drive.google.com/file/d/1koIUYVzYTn6tySnNjim-F6eKOf8xojo7/view?usp=drive_link"
+---
diff --git a/content/txts/direito-constitucional/_index.md b/content/txts/direito-constitucional/_index.md
new file mode 100644
index 0000000..2d7f57f
--- /dev/null
+++ b/content/txts/direito-constitucional/_index.md
@@ -0,0 +1,14 @@
+---
+title: "Direito constitucional"
+guide: |
+ ### Referências recomendadas
+ - Flávios Martins, por possuir linguagem mais simples;
+ - Marcelino Novelino, mais detalhado;
+ - Obras dos ministros (do STF) Barroso, Alexandre de Moraes e Gilmar Mendes (a linguagem deles é, geralmente, acessível, sem muito juridiquês).
+
+ Nos clássicos incluiu-se:
+ - Pendro Lenza (o esquematizado sobre Direito Constitucional);
+ - José Afonso da Silva (bem clássicos);
+ - Uadi Lammêgo Bulos;
+ - José Gomes Canotilho (teoria constitucional).
+---
diff --git a/content/txts/linguagem-juridica/_index.md b/content/txts/linguagem-juridica/_index.md
new file mode 100644
index 0000000..c4599ea
--- /dev/null
+++ b/content/txts/linguagem-juridica/_index.md
@@ -0,0 +1,6 @@
+---
+title: "Linguagem e argumentação jurídica"
+files:
+ - name: "Texto-base"
+ url: "https://drive.google.com/file/d/1X9rF9MQbdfP10fDTJbGFnK-AeGGCO2gL/view?usp=drive_link"
+---
diff --git a/content/txts/metodologia/_index.md b/content/txts/metodologia/_index.md
new file mode 100644
index 0000000..228d2bc
--- /dev/null
+++ b/content/txts/metodologia/_index.md
@@ -0,0 +1,6 @@
+---
+title: "Metodologia do trabalho científico"
+files:
+ - name: "Texto-base"
+ url: "https://drive.google.com/file/d/1lrtG3kkYaV-hCprSVuEacwiDytpOY3FO/view?usp=drive_link"
+---
diff --git a/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md b/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md
new file mode 100644
index 0000000..cd0a046
--- /dev/null
+++ b/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md
@@ -0,0 +1,6 @@
+---
+title: "Relações étnico-raciais e Direito humanos"
+guide: |
+ ### Referências recomendadas
+ - Direito humanos: André Ramos Tavares
+---
diff --git a/content/txts/sociologia-antropologia-juridica/_index.md b/content/txts/sociologia-antropologia-juridica/_index.md
new file mode 100644
index 0000000..8a8fcc2
--- /dev/null
+++ b/content/txts/sociologia-antropologia-juridica/_index.md
@@ -0,0 +1,6 @@
+---
+title: "Sociologia e Antropologia Jurídica"
+files:
+ - name: "Texto-base"
+ url: "https://drive.google.com/file/d/1hcbHhitTgH3qMkM-nPwBm9LkanzIlglj/view?usp=drive_link"
+---
diff --git a/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md b/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md
new file mode 100644
index 0000000..461c59a
--- /dev/null
+++ b/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md
@@ -0,0 +1,3 @@
+---
+title: Divisão técnica e social do trabalho
+---
diff --git a/content/txts/sociologia-antropologia-juridica/poder-dominacao.md b/content/txts/sociologia-antropologia-juridica/poder-dominacao.md
new file mode 100644
index 0000000..a93cc9d
--- /dev/null
+++ b/content/txts/sociologia-antropologia-juridica/poder-dominacao.md
@@ -0,0 +1,3 @@
+---
+title: Poder e dominação
+---
diff --git a/content/txts/teoria-historia-direito/_index.md b/content/txts/teoria-historia-direito/_index.md
new file mode 100644
index 0000000..c779573
--- /dev/null
+++ b/content/txts/teoria-historia-direito/_index.md
@@ -0,0 +1,8 @@
+---
+title: "Teoria e história do Direito"
+guide: |
+ ### Referências recomendadas
+ - Eduardo Bittar — principal da disciplina
+ - Flávio Lages — complementar (fora da biblioteca)
+ - Wolkmir — evite, está desatualizada
+---
diff --git a/content/txts/teoria-historia-direito/constituicoes.md b/content/txts/teoria-historia-direito/constituicoes.md
new file mode 100644
index 0000000..667d099
--- /dev/null
+++ b/content/txts/teoria-historia-direito/constituicoes.md
@@ -0,0 +1,30 @@
+---
+title: Comparação de constituições
+---
+
+| Ano | Forma de Estado | Forma de Governo | Separação de Poderes | Direitos e Deveres [^5] [^6] |
+| --- | --- | --- | --- | --- |
+| **1824** [^7] | Unitário | Monarquia [^8] | Quadripartite [^9] | *Não especificado* |
+| **1891** [^1] [^10] | Federativo | República [^11] | Tripartite [^12] | Individuais e políticos [^13] |
+| **1934** | Federativo | República | Tripartite | Individuais, políticos [^14] e sociais [^15] |
+| **1937** [^1] | Federativo | República | Tripartite | Retrocesso |
+| **1946** | Federativo | República | Tripartite | Restauração |
+| **1969** [^1] | Federativo | República | Tripartite | Retrocesso |
+| **1988** | Federativo | República | Tripartite | Individuais/coletivos, sociais, nacionalidade e políticos [^16] |
+
+[^1]: **Tipo de outorga:** 1891, 1937 e 1969 foram todos 'outorgados' (impostos) e não 'promulgados'.
+[^2]: **Forma de Estado:** Como o poder é determinado no território.
+[^3]: **Forma de Governo:** Como o poder é exercido.
+[^4]: **Separação de Poderes:** É um modelo político.
+[^5]: **Direitos:** É o "bem jurídico tutelado" (exigível/exercível).
+[^6]: **Deveres:** Todo 'direito' tem mecanismo de proteções ('remédios constitucionais').
+[^7]: **Contexto (1824):** A elite era a nobreza, já os marginalizados eram os escravizados, no geral.
+[^8]: **Monarquia:** Hereditariedade, vitaliciedade e irresponsabilidade (não-responder pessoalmente).
+[^9]: **Quadripartite:** Adiciona-se o "poder moderador".
+[^10]: **Contexto (1891):** A elite eram os militares e coronéis; já os marginalizados eram os imigrantes e ex-escravizados.
+[^11]: **República:** Efetividade, temporalidade e responsabilidade.
+[^12]: **Tripartite (1891):** Nominal, pois o executivo manda nos demais.
+[^13]: **Direitos (1891):** Porém desigual (sem voto de mulheres ou de analfabetos).
+[^14]: **Políticos (1934):** Adição de voto feminino; constitucionalizando o já previsto em 1932 (decreto nº 21.076).
+[^15]: **Sociais (1934):** Previsão de direitos trabalhistas.
+[^16]: **Políticos (1988):** Facilitasse e promovesse a filiação partidária (partidos políticos).
diff --git a/content/txts/teoria-historia-direito/direito-colonial.md b/content/txts/teoria-historia-direito/direito-colonial.md
new file mode 100644
index 0000000..a879409
--- /dev/null
+++ b/content/txts/teoria-historia-direito/direito-colonial.md
@@ -0,0 +1,8 @@
+---
+title: Direito colonial
+---
+
+O Direito brasileiro colonial não foi apenas um conjunto de regras importadas, mas sim uma complexa montagem de um aparelho jurídico e administrativo voltado para a manutenção do projeto colonial lusitano. Estruturado sobre os pilares político, jurídico e administrativo, o sistema evoluiu da fragmentação das capitanias hereditárias para a centralização do Governo-Geral, enquanto as Câmaras Municipais serviam como o elo vital para a gestão dos interesses locais e das elites regionais. Esse ordenamento operava dentro de uma lógica econômica mercantilista e era sustentado pelo regime escravagista, o que resultou em uma sociedade profundamente desigual, hierarquizada de forma vertical e rígida, onde o Direito servia para institucionalizar a exclusão.
+
+Juridicamente, o sistema era marcado por um caráter heterogêneo e confessional, fundindo o Direito português com o Direito Canônico (da Igreja) e as sucessivas Ordenações do Reino. Como a realidade da Colônia frequentemente ultrapassava as normas fixas, utilizavam-se leis extravagantes para atualizar as práticas, recorrendo-se ao Direito Romano para integrar as lacunas do sistema. O grande marco legislativo desse período foram as Ordenações Filipinas, que organizavam a vida social e institucional em cinco volumes distintos: o primeiro tratava da administração e organização judiciária; o segundo regulava a forte relação com a Igreja; o terceiro focava nos ritos processuais; o quarto nas relações civis e comerciais, como contratos; e o quinto definia o rigoroso sistema de crimes e penas.
+A finalidade última desse aparato não era a justiça social, mas a perpetuação do poder da Coroa e das elites, garantindo que a lei funcionasse como um instrumento de controle e estabilidade de um sistema pautado pela exploração e pela desigualdade.
diff --git a/content/txts/teoria-historia-direito/direito-contemporaneo.md b/content/txts/teoria-historia-direito/direito-contemporaneo.md
new file mode 100644
index 0000000..0a07667
--- /dev/null
+++ b/content/txts/teoria-historia-direito/direito-contemporaneo.md
@@ -0,0 +1,5 @@
+---
+title: Direito contemporâneo
+---
+
+O Direito contemporâneo consolidou-se como o estágio atual da evolução jurídica, manifestando-se plenamente no conceito de Estado Democrático de Direito, que fundamenta o sistema legal na soberania popular, garantindo o direito de votar e ser votado, e na estrita submissão de todos os poderes à lei. Este modelo surge como uma evolução necessária após a crise do Estado Social, sistema que buscava a igualdade material através da ampla intervenção estatal, mas que acabou declinando devido à falta de recursos financeiros para sustentar suas crescentes demandas e garantias sociais. Dessa forma, o Estado Democrático de Direito contemporâneo atua como uma síntese integradora que busca equilibrar a justiça social com a racionalidade econômica, reconhecendo que a viabilidade das normas e direitos depende da saúde financeira do Estado para que as promessas legais não se tornem vazias. Nesse contexto, o Direito é compreendido como uma produção cultural e um reflexo das exigências da sociedade, onde o ordenamento jurídico contemporâneo protege as liberdades individuais e promove a participação política, mantendo-se atento às limitações econômicas para garantir a estabilidade das instituições e a dignidade humana de forma clara e objetiva.
diff --git a/content/txts/teoria-historia-direito/direito-moderno.md b/content/txts/teoria-historia-direito/direito-moderno.md
new file mode 100644
index 0000000..16a0e0f
--- /dev/null
+++ b/content/txts/teoria-historia-direito/direito-moderno.md
@@ -0,0 +1,9 @@
+---
+title: "Direito moderno"
+---
+
+O Direito moderno surgiu da convergência entre o Humanismo, o Renascimento e a recepção técnica do Direito Romano, que serviu de base para a estruturação jurídica do Estado Moderno. Inicialmente, essa transição manifestou-se no Estado Absolutista, cujo auge na França de Luís XIV centralizou o poder nas mãos do monarca, criando um sistema sem direitos ou garantias individuais para os súditos, onde a vontade real prevalecia sobre qualquer norma.
+
+A ruptura com esse modelo ocorreu por meio das críticas do Iluminismo, com pensadores como Cesare Beccaria defendendo a reforma da justiça e a proporcionalidade das penas contra o arbítrio soberano. Esse movimento culminou no Estado Liberal, impulsionado pela Independência dos Estados Unidos e pela Revolução Francesa, que instituiu a Igualdade Formal e a proteção das liberdades individuais por meio de documentos como a Declaração dos Direitos do Homem e do Cidadão. Um marco fundamental dessa era foi o Código Civil Napoleônico de 1804, que sistematizou as normas civis sob uma ótica liberal e burguesa, utilizando a tradição técnica romana para garantir a segurança jurídica.
+
+Com o tempo, a percepção de que a igualdade perante a lei não corrigia as desigualdades sociais levou ao surgimento do Estado Social, que buscou a igualdade material através da intervenção estatal na economia e na proteção de direitos sociais. Contudo, esse modelo enfrentou crises profundas devido à falta de recursos para sustentar suas demandas. A evolução para o Direito Contemporâneo consolidou o Estado Democrático, fundamentado na soberania popular e no direito de votar e ser votado, culminando no conceito de Estado de Direito, que une os ideais liberais e sociais sob a estrita submissão de todos os poderes à lei.
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
new file mode 100644
index 0000000..4fca1f9
--- /dev/null
+++ b/layouts/_default/baseof.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="pt-BR">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="{{ .Site.Params.description }}">
+ <meta name="theme-color" content="#c9a961">
+ <link rel="manifest" href="{{ "/manifest.json" | relURL }}">
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect fill='%230f0f13' width='100' height='100'/><text x='50' y='75' font-size='60' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
+ <link rel="stylesheet" href="{{ "/main.css" | relURL }}">
+ <title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
+</head>
+<body>
+ <header>
+ {{ block "header" . }}
+ <a class="site-name" href="{{ "/" | relURL }}">direito</a>
+ {{ end }}
+ </header>
+ <main>
+ {{ block "main" . }}{{ end }}
+ </main>
+
+ <script src="{{ "/quiz-parser.js" | relURL }}"></script>
+ <script src="{{ "/quiz-engine.js" | relURL }}"></script>
+ <script src="{{ "/quiz-runtime.js" | relURL }}"></script>
+ <script src="{{ "/footnotes.js" | relURL }}"></script>
+ <script src="{{ "/home-filter.js" | relURL }}"></script>
+ <script src="{{ "/toc.js" | relURL }}"></script>
+ <script src="{{ "/dark-mode.js" | relURL }}"></script>
+ <script src="{{ "/keyboard-shortcuts.js" | relURL }}"></script>
+ <script src="{{ "/dashboard.js" | relURL }}"></script>
+
+ {{ block "scripts" . }}{{ end }}
+
+ <script>
+ if ('serviceWorker' in navigator) {
+ window.addEventListener('load', () => {
+ navigator.serviceWorker.register('/sw.js').catch(() => {});
+ });
+ }
+ </script>
+</body>
+</html>
diff --git a/layouts/_default/list.html b/layouts/_default/list.html
new file mode 100644
index 0000000..09804c9
--- /dev/null
+++ b/layouts/_default/list.html
@@ -0,0 +1,146 @@
+{{ define "title" }}
+ {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }}
+{{ end }}
+
+{{ define "header" }}
+ <a class="site-name" href="{{ "/" | relURL }}">direito</a>
+ {{ if not .IsHome }}
+ <span class="header-sep">/</span>
+ <span class="breadcrumb">{{ .Title }}</span>
+ {{ end }}
+{{ end }}
+
+{{ define "main" }}
+{{ if .IsHome }}
+
+ {{/* Mapas por slug de disciplina */}}
+ {{ $simMap := dict }}
+ {{ $quizMap := dict }}
+ {{ $discsQz := slice }}
+ {{ range .Site.RegularPages }}
+ {{ if eq .Section "quizzes" }}
+ {{ $slug := .File.Dir | strings.TrimSuffix "/" | path.Base }}
+ {{ if not (in $discsQz $slug) }}{{ $discsQz = $discsQz | append $slug }}{{ end }}
+ {{ $isSim := or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }}
+ {{ if $isSim }}
+ {{ $simMap = merge $simMap (dict $slug (add (index $simMap $slug | default 0) 1)) }}
+ {{ else }}
+ {{ $quizMap = merge $quizMap (dict $slug (add (index $quizMap $slug | default 0) 1)) }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+ <div class="disc-grid">
+ {{ $txtsSlugs := slice }}
+ {{ range .Site.Sections }}
+ {{ if eq .Section "txts" }}
+ {{ range .Sections }}
+ {{ $slug := .RelPermalink | strings.TrimSuffix "/" | path.Base }}
+ {{ $txtsSlugs = $txtsSlugs | append $slug }}
+ {{ $sims := index $simMap $slug | default 0 }}
+ {{ $qz := index $quizMap $slug | default 0 }}
+ <a class="disc-card fade" href="{{ .RelPermalink }}" data-filterable>
+ <div class="disc-card-name">{{ .Title }}</div>
+ <div class="disc-card-meta">
+ {{ $n := len .RegularPages }}
+ {{ if $n }}{{ $n }} assunto{{ if gt $n 1 }}s{{ end }}{{ else }}sem textos{{ end }}
+ {{ if $sims }} · simulado{{ end }}
+ {{ if $qz }} · {{ $qz }} exercício{{ if gt $qz 1 }}s{{ end }}{{ end }}
+ </div>
+ </a>
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+ {{/* Disciplinas que só existem em quizzes (sem txts) */}}
+ {{ range $slug := $discsQz }}
+ {{ if not (in $txtsSlugs $slug) }}
+ {{ $sims := index $simMap $slug | default 0 }}
+ {{ $qz := index $quizMap $slug | default 0 }}
+ <a class="disc-card fade" href="/{{ $slug }}/" data-filterable>
+ <div class="disc-card-name">{{ humanize (replace $slug "-" " ") }}</div>
+ <div class="disc-card-meta">
+ {{ if $sims }}simulado · {{ end }}{{ $qz }} exercício{{ if gt $qz 1 }}s{{ end }}
+ </div>
+ </a>
+ {{ end }}
+ {{ end }}
+ </div>
+
+{{ else }}
+ {{/* Página de disciplina */}}
+ {{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Base }}
+ <div class="discipline fade">
+ <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div>
+ <div class="discipline-name">{{ .Title }}</div>
+
+ {{ if .Content }}
+ <div class="disc-intro">
+ <article class="prose disc-intro-prose">{{ .Content }}</article>
+ </div>
+ {{ end }}
+
+ <div class="disc-extras-wrap">
+ {{ partial "disc-extras.html" . }}
+ </div>
+
+ {{/* Simulados desta disciplina */}}
+ {{ $simulados := slice }}
+ {{ range $.Site.RegularPages }}
+ {{ if eq .Section "quizzes" }}
+ {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }}
+ {{ if eq $parent $discSlug }}
+ {{ if or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }}
+ {{ $simulados = $simulados | append . }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{ with $simulados }}
+ <div class="subject-group" style="margin-bottom:1.5rem;">
+ <div class="sq-label" style="margin-bottom:.65rem;">simulados</div>
+ <div class="sq-list">
+ {{ range . }}
+ <a class="sq-item" href="{{ .RelPermalink }}">
+ <span class="sq-name" style="color:var(--amber);">{{ .Title }}</span>
+ <span class="sq-meta">simulado</span>
+ </a>
+ {{ end }}
+ </div>
+ </div>
+ {{ end }}
+
+ {{/* Assuntos com vínculo aos exercícios */}}
+ {{ $exercises := slice }}
+ {{ range $.Site.RegularPages }}
+ {{ if eq .Section "quizzes" }}
+ {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }}
+ {{ if eq $parent $discSlug }}
+ {{ if not (or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado")) }}
+ {{ $exercises = $exercises | append . }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+
+ <div class="subject-list">
+ {{ range .RegularPages }}
+ {{ $assuntoSlug := .File.BaseFileName }}
+ {{ $linkedQuiz := false }}
+ {{ range $exercises }}
+ {{ if eq .File.BaseFileName $assuntoSlug }}
+ {{ $linkedQuiz = . }}
+ {{ end }}
+ {{ end }}
+ <a class="subject-row" href="{{ .RelPermalink }}">
+ <span class="subject-row-name">{{ .Title }}</span>
+ <span class="subject-row-meta">
+ texto{{ if $linkedQuiz }} · exercícios{{ end }}
+ </span>
+ </a>
+ {{ end }}
+ {{ if not .RegularPages }}<p class="empty-note">sem assuntos ainda</p>{{ end }}
+ </div>
+ </div>
+{{ end }}
+{{ end }}
diff --git a/layouts/partials/disc-extras.html b/layouts/partials/disc-extras.html
new file mode 100644
index 0000000..adff907
--- /dev/null
+++ b/layouts/partials/disc-extras.html
@@ -0,0 +1,72 @@
+{{/* disc-extras.html — recebe . (a página, seja _index ou single)
+ Renderiza colapsáveis: Guia de Estudo, Materiais (com subdivisões: Textos, Vídeos, Áudios) */}}
+
+{{/* ── Guia de estudo (guide) ── */}}
+{{ with .Params.guide }}
+<details class="disc-extra">
+ <summary class="disc-extra-summary">
+ <span class="disc-extra-label">guia de estudo</span>
+ <span class="disc-extra-chevron">↓</span>
+ </summary>
+ <div class="disc-extra-body disc-extra-prose">
+ {{ . | markdownify }}
+ </div>
+</details>
+{{ end }}
+
+{{/* ── Materiais unificados (files, videos, audios) ── */}}
+{{ $files := .Params.files }}
+{{ $videos := .Params.videos }}
+{{ $audios := .Params.audios }}
+{{ if or $files (or $videos $audios) }}
+<details class="disc-extra">
+ <summary class="disc-extra-summary">
+ <span class="disc-extra-label">materiais</span>
+ <span class="disc-extra-count">
+ {{ $n := 0 }}
+ {{ with $files }}{{ $n = add $n (len .) }}{{ end }}
+ {{ with $videos }}{{ $n = add $n (len .) }}{{ end }}
+ {{ with $audios }}{{ $n = add $n (len .) }}{{ end }}
+ {{ $n }} item{{ if gt $n 1 }}s{{ end }}
+ </span>
+ <span class="disc-extra-chevron">↓</span>
+ </summary>
+ <div class="disc-extra-body">
+ {{/* Textos (arquivos/links) */}}
+ {{ with $files }}
+ <div class="disc-extra-kind-label">textos</div>
+ {{ range . }}
+ <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener">
+ <span class="disc-extra-icon">↗</span>
+ <span class="disc-extra-name">{{ .name }}</span>
+ <span class="disc-extra-meta">link</span>
+ </a>
+ {{ end }}
+ {{ end }}
+
+ {{/* Vídeos */}}
+ {{ with $videos }}
+ <div class="disc-extra-kind-label">vídeos</div>
+ {{ range . }}
+ <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener">
+ <span class="disc-extra-icon">▶</span>
+ <span class="disc-extra-name">{{ .name }}</span>
+ <span class="disc-extra-meta">youtube</span>
+ </a>
+ {{ end }}
+ {{ end }}
+
+ {{/* Áudios */}}
+ {{ with $audios }}
+ <div class="disc-extra-kind-label">áudios</div>
+ {{ range . }}
+ <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener">
+ <span class="disc-extra-icon">♪</span>
+ <span class="disc-extra-name">{{ .name }}</span>
+ <span class="disc-extra-meta">youtube</span>
+ </a>
+ {{ end }}
+ {{ end }}
+ </div>
+</details>
+{{ end }}
diff --git a/layouts/partials/quiz-runtime.html b/layouts/partials/quiz-runtime.html
new file mode 100644
index 0000000..9ce5aab
--- /dev/null
+++ b/layouts/partials/quiz-runtime.html
@@ -0,0 +1,447 @@
+<script>
+/* ── ESTADO DA APLICAÇÃO ── */
+let currentQuiz = null;
+/* questions, qIndex, acertos, erros, answered → usam _qState (quiz-engine.html) */
+
+const BASE_TITLE = document.title;
+
+let activeFloater = null;
+let currentTextComments = {};
+let lastSubjectRoute = null;
+
+/* ── UTILITÁRIOS ── */
+/* shuffle, esc, md → quiz-parser.html | _qState, _renderQuestion, etc. → quiz-engine.html */
+
+function slugifyText(s) {
+ return String(s || '')
+ .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
+ .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
+}
+
+function show(id) {
+ ['page-home','page-quiz','page-result','page-text'].forEach(p => {
+ const el = document.getElementById(p);
+ if (el) el.classList.toggle('active', p === id);
+ });
+}
+
+/* ── ROTEAMENTO ── */
+function encodeHash(type, a, b, c) {
+ if (type === 'home') return '';
+ if (type === 'disc') return '#disc/' + encodeURIComponent(a);
+ if (type === 'assunto') return '#assunto/' + [a, b].map(encodeURIComponent).join('/');
+ if (type === 'quiz') return '#quiz/' + [a, b, c].map(encodeURIComponent).join('/');
+ if (type === 'simulado') return '#simulado/' + [a, b].map(encodeURIComponent).join('/');
+ return '';
+}
+
+function decodeHash(hash) {
+ if (!hash || hash === '#') return { type: 'home' };
+ const parts = (hash.startsWith('#') ? hash.slice(1) : hash)
+ .split('/').map(s => { try { return decodeURIComponent(s); } catch(e) { return s; } });
+
+ const type = parts[0];
+ if (type === 'disc' && parts.length >= 2)
+ return { type: 'disc', disc: parts[1] };
+ if (type === 'assunto' && parts.length >= 3)
+ return { type: 'assunto', discFolder: parts[1], slug: parts[2] };
+ if (type === 'quiz' && parts.length >= 4)
+ return { type: 'quiz', discFolder: parts[1], slug: parts[2], quizId: parts[3] };
+ if (type === 'simulado' && parts.length >= 3)
+ return { type: 'simulado', discFolder: parts[1], quizId: parts[2] };
+
+ return { type: 'home' };
+}
+
+function navigate(hash, replace) {
+ const url = window.location.pathname + window.location.search + (hash || '');
+ if (replace) history.replaceState({ hash }, '', url || window.location.pathname);
+ else history.pushState({ hash }, '', url || window.location.pathname);
+}
+
+function renderRoute(route) {
+ if (route.type === 'disc') _showDisc(route.disc);
+ else if (route.type === 'assunto') _openSubject(route.discFolder, route.slug);
+ else if (route.type === 'quiz') _startQuiz(route.discFolder, route.slug, route.quizId);
+ else if (route.type === 'simulado')_startSimulado(route.discFolder, route.quizId);
+ else _showHome();
+}
+
+window.addEventListener('popstate', () => renderRoute(decodeHash(window.location.hash)));
+
+/* ── INTERFACE: HOME ── */
+function showHome() { navigate('', false); _showHome(); }
+
+function _showHome() {
+ closeFloater();
+ buildHome();
+ show('page-home');
+ document.getElementById('breadcrumb').textContent = '';
+ document.title = BASE_TITLE;
+}
+
+function buildHome() {
+ const el = document.getElementById('page-home');
+ el.innerHTML = `
+ <div class="disc-grid">
+ ${DATA.map(disc => {
+ const n = disc.subjects.length;
+ const nq = disc.subjects.reduce((acc, s) => acc + s.quizzes.length, 0) + (disc.quizzes ? disc.quizzes.length : 0);
+ const meta = [
+ n ? `${n} assunto${n > 1 ? 's' : ''}` : null,
+ nq ? `${nq} quiz${nq > 1 ? 'zes' : ''}` : null,
+ ].filter(Boolean).join(' · ') || 'sem conteúdo';
+ return `
+ <a class="disc-card fade" href="${encodeHash('disc', disc.discipline)}"
+ onclick="openDisc('${esc(disc.discipline)}');return false;">
+ <div class="disc-card-name">${esc(disc.discipline)}</div>
+ <div class="disc-card-meta">${meta}</div>
+ </a>`;
+ }).join('')}
+ </div>`;
+}
+
+/* ── INTERFACE: DISCIPLINA ── */
+function openDisc(discName) { navigate(encodeHash('disc', discName), false); _showDisc(discName); }
+
+function _showDisc(discName) {
+ closeFloater();
+ const disc = DATA.find(d => d.discipline === discName);
+ if (!disc) { _showHome(); return; }
+
+ const introHtml = disc.intro && disc.intro.content
+ ? `<div class="disc-intro">
+ <article class="prose disc-intro-prose">${disc.intro.content}</article>
+ ${disc.intro.references && disc.intro.references.length
+ ? `<div class="references disc-intro-refs">
+ <div class="ref-head">referências</div>
+ <div class="ref-list">${disc.intro.references.map(r => `<p>${esc(r)}</p>`).join('')}</div>
+ </div>`
+ : ''}
+ </div>`
+ : '';
+
+ const simuladosHtml = disc.quizzes && disc.quizzes.length ? `
+ <div class="subject-group">
+ <div class="sq-label" style="margin-bottom:10px;">Simulados da Disciplina</div>
+ ${disc.quizzes.map(qz => `
+ <a class="sq-item" href="${encodeHash('simulado', disc.folderKey, qz.id)}"
+ onclick="startSimulado('${esc(disc.folderKey)}','${esc(qz.id)}');return false;"
+ style="border-bottom:none; background:var(--surface); padding: 10px; border-radius: 4px; margin-bottom: 5px;">
+ <span class="sq-name" style="color:var(--amber); font-weight:bold;">${esc(qz.title)}</span>
+ <span class="sq-meta">${qz.questions.length} questões</span>
+ </a>
+ `).join('')}
+ </div>
+ ` : '';
+
+ const subjectsHtml = disc.subjects.map(s => {
+ const bits = [];
+ if (s.text) bits.push('texto');
+ if (s.quizzes.length) bits.push(`${s.quizzes.length} quiz${s.quizzes.length > 1 ? 'zes' : ''}`);
+ const meta = bits.join(' · ') || 'vazio';
+ return `
+ <a class="subject-row" href="${encodeHash('assunto', disc.folderKey, s.slug)}"
+ onclick="openSubject('${esc(disc.folderKey)}','${esc(s.slug)}');return false;">
+ <span class="subject-row-name">${esc(s.name)}</span>
+ <span class="subject-row-meta">${meta}</span>
+ </a>`;
+ }).join('');
+
+ const el = document.getElementById('page-home');
+ el.innerHTML = `
+ <div class="discipline fade">
+ <div class="disc-back"><a href="#" onclick="showHome();return false;">← disciplinas</a></div>
+ <div class="discipline-name">${esc(disc.discipline)}</div>
+ ${introHtml}
+ ${simuladosHtml}
+ <div class="subject-list">${subjectsHtml || '<p class="empty-note">sem assuntos ainda</p>'}</div>
+ </div>`;
+
+ show('page-home');
+ document.getElementById('breadcrumb').textContent = disc.discipline;
+ document.title = disc.discipline + ' · ' + BASE_TITLE;
+}
+
+/* ── INTERFACE: ASSUNTO ── */
+function openSubject(discFolder, slug) {
+ navigate(encodeHash('assunto', discFolder, slug), false);
+ _openSubject(discFolder, slug);
+}
+
+function _openSubject(discFolder, slug) {
+ closeFloater();
+ const disc = DATA.find(d => d.folderKey === discFolder);
+ if (!disc) { _showHome(); return; }
+ const sub = disc.subjects.find(s => s.slug === slug);
+ if (!sub) { _showDisc(disc.discipline); return; }
+
+ lastSubjectRoute = { discFolder, slug, discName: disc.discipline, subjectName: sub.name };
+
+ document.getElementById('text-discipline').textContent = disc.discipline;
+ document.getElementById('text-title').textContent = sub.name;
+
+ const qzWrap = document.getElementById('text-quizzes');
+ const qzList = document.getElementById('text-quizzes-list');
+
+ if (sub.quizzes.length) {
+ qzWrap.style.display = '';
+ qzList.innerHTML = sub.quizzes.map(qz => {
+ const mc = qz.questions.filter(q => q.type === 'mc').length;
+ const open = qz.questions.filter(q => q.type === 'open').length;
+ const meta = [
+ mc ? `${mc} mc` : null,
+ open ? `${open} abertas` : null,
+ ].filter(Boolean).join(' · ') || `${qz.questions.length} questões`;
+ return `
+ <a class="sq-item" href="${encodeHash('quiz', discFolder, slug, qz.id)}"
+ onclick="startQuiz('${esc(discFolder)}','${esc(slug)}','${esc(qz.id)}');return false;">
+ <span class="sq-name">${esc(qz.title)}</span>
+ <span class="sq-meta">${meta}</span>
+ </a>`;
+ }).join('');
+ } else {
+ qzWrap.style.display = 'none';
+ qzList.innerHTML = '';
+ }
+
+ const prose = document.getElementById('prose');
+ if (sub.text && sub.text.content) {
+ prose.innerHTML = sub.text.content;
+ currentTextComments = {};
+ buildToc(prose);
+ } else {
+ prose.innerHTML = '<p class="empty-note">Texto ainda não disponível para este assunto.</p>';
+ document.getElementById('text-toc').style.display = 'none';
+ }
+
+ const refContainer = document.getElementById('text-references-container');
+ const refList = document.getElementById('text-references-list');
+ const refs = sub.text ? (sub.text.references || []) : [];
+
+ if (refs.length) {
+ refContainer.style.display = 'block';
+ refList.innerHTML = refs.map(r => `<p>${esc(r)}</p>`).join('');
+ } else {
+ refContainer.style.display = 'none';
+ }
+
+ document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${sub.name}`;
+ document.title = sub.name + ' · ' + BASE_TITLE;
+ show('page-text');
+ window.scrollTo(0, 0);
+}
+
+/* ── INTERFACE: SUMÁRIO (TOC) ── */
+function buildToc(proseEl) {
+ const tocWrap = document.getElementById('text-toc');
+ const tocList = document.getElementById('text-toc-list');
+ tocList.innerHTML = '';
+
+ const headings = proseEl.querySelectorAll('h2, h3');
+ if (!headings.length) { tocWrap.style.display = 'none'; return; }
+
+ const used = new Set();
+ headings.forEach(h => {
+ if (!h.id) {
+ let base = slugifyText(h.textContent) || 'sec';
+ let id = base, i = 2;
+ while (used.has(id) || document.getElementById(id)) { id = base + '-' + i++; }
+ h.id = id;
+ }
+ used.add(h.id);
+ });
+
+ headings.forEach(h => {
+ const li = document.createElement('li');
+ li.className = 'toc-' + h.tagName.toLowerCase();
+ const a = document.createElement('a');
+ a.href = '#' + h.id;
+ a.textContent = h.textContent;
+ a.addEventListener('click', e => {
+ e.preventDefault();
+ h.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ history.replaceState(history.state, '', '#' + h.id);
+ });
+ li.appendChild(a);
+ tocList.appendChild(li);
+ });
+
+ tocWrap.style.display = '';
+}
+
+/* ── INTERFACE: COMPONENTES FLUTUANTES ── */
+document.addEventListener('click', e => {
+ const footnoteLink = e.target.closest('a[href^="#fn:"]');
+ const closeBtn = e.target.closest('.f-close');
+
+ if (closeBtn) { closeFloater(); return; }
+
+ if (footnoteLink) {
+ e.preventDefault(); e.stopPropagation(); closeFloater();
+ const targetId = footnoteLink.getAttribute('href').substring(1);
+ const footnoteElement = document.getElementById(targetId);
+ if (footnoteElement) {
+ const contentClone = footnoteElement.cloneNode(true);
+ const backLink = contentClone.querySelector('.footnote-backref');
+ if (backLink) backLink.remove();
+ const parentParagraph = footnoteLink.closest('p, li');
+ const quoteText = parentParagraph ? parentParagraph.innerText.substring(0, 40) + '…' : 'Nota';
+ openFloater(footnoteLink, quoteText, contentClone.innerHTML);
+ footnoteLink.classList.add('active');
+ }
+ } else if (activeFloater && !e.target.closest('.floater')) {
+ closeFloater();
+ }
+});
+
+function openFloater(target, quote, innerHTMLContent) {
+ const f = document.createElement('div');
+ f.className = 'floater';
+ f.innerHTML = `
+ <div class="f-label"><span>comentário</span><span class="f-close">×</span></div>
+ <div class="f-quote">${quote}</div>
+ <div>${innerHTMLContent}</div>`;
+ document.body.appendChild(f);
+
+ const r = target.getBoundingClientRect();
+ const top = window.scrollY + r.bottom + 8;
+ const padding = 20;
+ let left = window.scrollX + r.left;
+ const maxLeft = window.innerWidth - f.offsetWidth - padding;
+
+ if (left > maxLeft) left = Math.max(padding, maxLeft);
+
+ f.style.top = top + 'px';
+ f.style.left = left + 'px';
+ activeFloater = f;
+}
+
+function closeFloater() {
+ if (activeFloater) { activeFloater.remove(); activeFloater = null; }
+ document.querySelectorAll('a[href^="#fn:"]').forEach(a => a.classList.remove('active'));
+}
+
+document.addEventListener('keydown', e => { if (e.key === 'Escape') closeFloater(); });
+
+/* ── MOTOR: QUIZ E SIMULADO ── */
+function startQuiz(discFolder, slug, quizId) {
+ navigate(encodeHash('quiz', discFolder, slug, quizId), false);
+ _startQuiz(discFolder, slug, quizId);
+}
+
+function _startQuiz(discFolder, slug, quizId) {
+ closeFloater();
+ const disc = DATA.find(d => d.folderKey === discFolder);
+ if (!disc) { _showHome(); return; }
+ const sub = disc.subjects.find(s => s.slug === slug);
+ if (!sub) { _showDisc(disc.discipline); return; }
+ const quiz = sub.quizzes.find(q => q.id === quizId);
+ if (!quiz) { _openSubject(discFolder, slug); return; }
+
+ lastSubjectRoute = { discFolder, slug, discName: disc.discipline, subjectName: sub.name };
+ currentQuiz = {
+ isSimulado: false, discFolder, slug, quizId,
+ subjectName: sub.name, quizTitle: quiz.title
+ };
+
+ _qState.questions = prepareQuizQuestions(quiz.questions);
+ _qState.qIndex = _qState.acertos = _qState.erros = 0;
+ _clearProgress('spa_' + discFolder + '_' + slug + '_' + quizId);
+
+ document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${sub.name} · ${quiz.title}`;
+ document.title = `${quiz.title} · ${sub.name} · ${BASE_TITLE}`;
+
+ show('page-quiz');
+ renderQuestion();
+}
+
+function startSimulado(discFolder, quizId) {
+ navigate(encodeHash('simulado', discFolder, quizId), false);
+ _startSimulado(discFolder, quizId);
+}
+
+function _startSimulado(discFolder, quizId) {
+ closeFloater();
+ const disc = DATA.find(d => d.folderKey === discFolder);
+ if (!disc) { _showHome(); return; }
+ const quiz = disc.quizzes.find(q => q.id === quizId);
+ if (!quiz) { _showDisc(disc.discipline); return; }
+
+ lastSubjectRoute = null;
+ currentQuiz = {
+ isSimulado: true, discFolder, quizId,
+ subjectName: disc.discipline, quizTitle: quiz.title
+ };
+
+ _qState.questions = prepareQuizQuestions(quiz.questions);
+ _qState.qIndex = _qState.acertos = _qState.erros = 0;
+ _clearProgress('spa_sim_' + discFolder + '_' + quizId);
+
+ document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${quiz.title}`;
+ document.title = `${quiz.title} · ${disc.discipline} · ${BASE_TITLE}`;
+
+ show('page-quiz');
+ renderQuestion();
+}
+
+function restartQuiz() {
+ if (currentQuiz.isSimulado) {
+ _clearProgress('spa_sim_' + currentQuiz.discFolder + '_' + currentQuiz.quizId);
+ _startSimulado(currentQuiz.discFolder, currentQuiz.quizId);
+ } else {
+ _clearProgress('spa_' + currentQuiz.discFolder + '_' + currentQuiz.slug + '_' + currentQuiz.quizId);
+ _startQuiz(currentQuiz.discFolder, currentQuiz.slug, currentQuiz.quizId);
+ }
+}
+
+function backToSubject() {
+ if (currentQuiz && currentQuiz.isSimulado) {
+ openDisc(currentQuiz.subjectName);
+ } else if (lastSubjectRoute) {
+ navigate(encodeHash('assunto', lastSubjectRoute.discFolder, lastSubjectRoute.slug), false);
+ _openSubject(lastSubjectRoute.discFolder, lastSubjectRoute.slug);
+ } else {
+ showHome();
+ }
+}
+
+function renderQuestion() {
+ _renderQuestion(_qState.questions[_qState.qIndex]);
+ const progKey = currentQuiz
+ ? 'spa_' + (currentQuiz.isSimulado
+ ? 'sim_' + currentQuiz.discFolder + '_' + currentQuiz.quizId
+ : currentQuiz.discFolder + '_' + currentQuiz.slug + '_' + currentQuiz.quizId)
+ : null;
+ if (progKey) _saveProgress(progKey);
+}
+
+/* selectOpt, showRef, selfJudge, setFeedback → definidos em quiz-parser.html */
+
+function nextQuestion() {
+ _qState.qIndex++;
+ if (_qState.qIndex >= _qState.questions.length) { showResult(); return; }
+ const body = document.getElementById('quiz-body');
+ body.classList.add('fade-out');
+ setTimeout(() => { body.classList.remove('fade-out'); renderQuestion(); }, 100);
+}
+
+/* ── TELA DE RESULTADOS ── */
+function showResult() {
+ show('page-result');
+ _buildResult();
+ document.getElementById('breadcrumb').textContent = currentQuiz.subjectName + ' · ' + currentQuiz.quizTitle;
+}
+
+/* ── INICIALIZAÇÃO DE ROTA ── */
+(function init() {
+ const route = decodeHash(window.location.hash);
+ if (route.type !== 'home') {
+ history.replaceState({ hash: window.location.hash }, '', window.location.href);
+ renderRoute(route);
+ } else {
+ history.replaceState({ hash: '' }, '', window.location.pathname + window.location.search);
+ _showHome();
+ }
+})();
+</script>
diff --git a/layouts/quizzes/list.html b/layouts/quizzes/list.html
new file mode 100644
index 0000000..c37a486
--- /dev/null
+++ b/layouts/quizzes/list.html
@@ -0,0 +1,57 @@
+{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }}
+
+{{ define "header" }}
+ <a class="site-name" href="{{ "/" | relURL }}">direito</a>
+ <span class="header-sep">/</span>
+ <span class="breadcrumb">{{ .Title }}</span>
+{{ end }}
+
+{{ define "main" }}
+{{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Base }}
+<div class="discipline fade">
+ <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div>
+ <div class="discipline-name">{{ .Title }}</div>
+
+ {{/* Separa simulados (frontmatter `simulado: true` OU nome contendo "simulado") dos quizzes
+ desta disciplina, usando .File.Dir já que as subpastas de quizzes/ não são sections. */}}
+ {{ $simulados := slice }}
+ {{ $quizzes := slice }}
+ {{ range .RegularPages }}
+ {{ if or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }}
+ {{ $simulados = $simulados | append . }}
+ {{ else }}
+ {{ $quizzes = $quizzes | append . }}
+ {{ end }}
+ {{ end }}
+
+ {{ with $simulados }}
+ <div class="subject-group" style="margin-bottom:1.5rem;">
+ <div class="sq-label" style="margin-bottom:.65rem;">simulados</div>
+ <div class="sq-list">
+ {{ range . }}
+ <a class="sq-item" href="{{ .RelPermalink }}">
+ <span class="sq-name" style="color:var(--amber);">{{ .Title }}</span>
+ <span class="sq-meta">simulado</span>
+ </a>
+ {{ end }}
+ </div>
+ </div>
+ {{ end }}
+
+ {{ with $quizzes }}
+ <div class="subject-list">
+ <div class="sq-label" style="margin-bottom:.65rem;">exercícios</div>
+ {{ range . }}
+ <a class="subject-row" href="{{ .RelPermalink }}">
+ <span class="subject-row-name">{{ .Title }}</span>
+ <span class="subject-row-meta">quiz</span>
+ </a>
+ {{ end }}
+ </div>
+ {{ end }}
+
+ {{ if and (not $simulados) (not $quizzes) }}
+ <p class="empty-note">sem conteúdo ainda</p>
+ {{ end }}
+</div>
+{{ end }}
diff --git a/layouts/quizzes/single.html b/layouts/quizzes/single.html
new file mode 100644
index 0000000..0a51a9c
--- /dev/null
+++ b/layouts/quizzes/single.html
@@ -0,0 +1,60 @@
+{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }}
+{{ define "header" }}
+ {{ $discSlug := .File.Dir | path.Dir | path.Base }}
+ <a class="site-name" href="{{ "/" | relURL }}">direito</a>
+ <span class="header-sep">/</span>
+ {{ $discPage := $.Site.GetPage (printf "/txts/%s" $discSlug) }}
+ {{ if $discPage }}
+ <a class="breadcrumb-link" href="{{ $discPage.RelPermalink }}">{{ $discPage.Title }}</a>
+ <span class="header-sep">/</span>
+ {{ end }}
+ <span class="breadcrumb">{{ .Title }}</span>
+{{ end }}
+{{ define "main" }}
+{{ $discSlug := .File.Dir | path.Dir | path.Base }}
+{{ $discPage := $.Site.GetPage (printf "/txts/%s" $discSlug) }}
+<div class="fade">
+ {{ if $discPage }}
+ <div class="disc-back"><a href="{{ $discPage.RelPermalink }}">← {{ $discPage.Title }}</a></div>
+ {{ else }}
+ <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div>
+ {{ end }}
+ <div class="discipline-name" style="margin-bottom:1.5rem;">{{ .Title }}</div>
+ <div id="quiz-wrap">
+ <div class="progress-wrap"><div class="progress-fill" id="prog"></div></div>
+ <div class="q-meta" id="q-counter"></div>
+ <div id="quiz-body"></div>
+ <div class="next-row">
+ <button class="btn" id="btn-next" onclick="nextQuestion()" style="display:none">próxima →</button>
+ </div>
+ </div>
+ <div id="quiz-result" style="display:none;">
+ <div class="res-score" id="res-pct">0%</div>
+ <div class="res-subtitle"><span id="res-a">0</span> de <span id="res-t">0</span> acertos</div>
+ <div class="res-divider"></div>
+ <div class="res-stats">
+ <div class="rs-item"><div class="rs-num g" id="res-a2">0</div><div class="rs-label">acertos</div></div>
+ <div class="rs-item"><div class="rs-num r" id="res-e">0</div><div class="rs-label">erros</div></div>
+ </div>
+ <div class="res-msg">
+ <div class="res-msg-label">diagnóstico</div>
+ <div id="res-msg-body"></div>
+ </div>
+ <div class="res-btns">
+ <button class="btn" onclick="restartQuiz()">refazer</button>
+ {{ if $discPage }}
+ <a class="btn" href="{{ $discPage.RelPermalink }}">voltar</a>
+ {{ end }}
+ </div>
+ </div>
+</div>
+
+<script>
+ // Definir a chave única para este quiz
+ window.QUIZ_KEY = '{{ $discSlug }}_{{ .File.BaseFileName }}';
+ // Conteúdo bruto do quiz em JSON (sem notas de rodapé Markdown)
+ const RAW_CONTENT = {{ .RawContent | jsonify | safeJS }};
+ // Remover notas de rodapé Markdown ([^1]: ...)
+ const RAW_QUIZ = RAW_CONTENT.replace(/\n\[\^\d+\]:[^\n]*/g, '');
+</script>
+{{ end }}
diff --git a/layouts/txts/single.html b/layouts/txts/single.html
new file mode 100644
index 0000000..d8f7eb1
--- /dev/null
+++ b/layouts/txts/single.html
@@ -0,0 +1,224 @@
+{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }}
+
+{{ define "header" }}
+ {{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Dir | path.Base }}
+ <a class="site-name" href="{{ "/" | relURL }}">direito</a>
+ {{ range $.Site.Sections }}
+ {{ if and (eq .Section "txts") (eq (.RelPermalink | strings.TrimSuffix "/" | path.Base) $discSlug) }}
+ <span class="header-sep">/</span>
+ <a class="breadcrumb-link" href="{{ .RelPermalink }}">{{ .Title }}</a>
+ {{ end }}
+ {{ end }}
+ <span class="header-sep">/</span>
+ <span class="breadcrumb">{{ .Title }}</span>
+{{ end }}
+
+{{ define "main" }}
+{{ $slug := .File.BaseFileName }}
+{{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Dir | path.Base }}
+
+{{/* Encontra quiz do assunto (exercício, não simulado) */}}
+{{ $quizPage := false }}
+{{ range $.Site.RegularPages }}
+ {{ if eq .Section "quizzes" }}
+ {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }}
+ {{ if and (eq $parent $discSlug) (eq .File.BaseFileName $slug) }}
+ {{ if not (or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado")) }}
+ {{ $quizPage = . }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+{{ end }}
+
+<div class="page-text fade">
+ <div class="text-meta">
+ {{ range $.Site.Sections }}
+ {{ if and (eq .Section "txts") (eq (.RelPermalink | strings.TrimSuffix "/" | path.Base) $discSlug) }}
+ <span>{{ .Title }}</span><span class="dot"></span>
+ {{ end }}
+ {{ end }}
+ <span>assunto</span>
+ </div>
+ <h1 class="text-title">{{ .Title }}</h1>
+
+ {{/* Colapsáveis: guia de estudo e materiais do assunto */}}
+ {{ $hasExtras := or .Params.guide (or .Params.files (or .Params.videos .Params.audios)) }}
+ {{ if $hasExtras }}
+ <div class="disc-extras-wrap">
+ {{ partial "disc-extras.html" . }}
+ </div>
+ {{ end }}
+
+ <nav class="toc" id="toc-nav" style="display:none;" aria-label="Sumário">
+ <div class="toc-label">sumário</div>
+ <ol id="toc-list"></ol>
+ </nav>
+
+ <article class="prose" id="prose">{{ .Content }}</article>
+
+ {{/* Notas de rodapé — renderizadas pelo Hugo automaticamente no .Content;
+ o floater (footnotes.js) intercepta os links #fn: e mostra tooltip */}}
+
+ {{/* Questões do assunto — quiz inline embutido */}}
+ {{ if $quizPage }}
+ <div class="inline-quiz-wrap" id="inline-quiz-section">
+ <div class="inline-quiz-head">
+ <span class="sq-label">exercícios deste assunto</span>
+ <a class="inline-quiz-link" href="{{ $quizPage.RelPermalink }}">modo completo →</a>
+ </div>
+ <div id="inline-quiz-body">
+ <button class="btn btn-start-quiz" id="btn-start-inline" onclick="startInlineQuiz()">
+ Iniciar exercícios
+ </button>
+ </div>
+ <div id="inline-quiz-player" style="display:none;">
+ <div class="progress-wrap" style="margin-bottom:.75rem;"><div class="progress-fill" id="iprog"></div></div>
+ <div class="q-meta" id="iq-counter"></div>
+ <div id="iq-body"></div>
+ <div class="next-row">
+ <button class="btn" id="ibtn-next" onclick="inlineNextQuestion()" style="display:none">próxima →</button>
+ </div>
+ </div>
+ <div id="inline-quiz-result" style="display:none;">
+ <div class="res-score" id="ires-pct">0%</div>
+ <div class="res-subtitle"><span id="ires-a">0</span> de <span id="ires-t">0</span> acertos</div>
+ <div class="res-msg" style="margin-top:1rem;">
+ <div class="res-msg-label">diagnóstico</div>
+ <div id="ires-msg"></div>
+ </div>
+ <div class="res-btns" style="margin-top:1.2rem;">
+ <button class="btn" onclick="restartInlineQuiz()">refazer</button>
+ <a class="btn" href="{{ $quizPage.RelPermalink }}">modo completo</a>
+ </div>
+ </div>
+ </div>
+ <script>
+ (function() {
+ const RAW = {{ $quizPage.RawContent | jsonify | safeJS }};
+ // Remove frontmatter YAML e notas de rodapé
+ const body = RAW.replace(/^---[\s\S]*?---\n/, '').replace(/\n\[\^\d+\]:[^\n]*/g, '');
+ window._IQ = { raw: body, questions: [], idx: 0, acertos: 0, erros: 0, answered: false, flipped: false };
+ })();
+
+ function startInlineQuiz() {
+ document.getElementById('btn-start-inline').style.display = 'none';
+ const qs = prepareQuizQuestions(parseQuizBody(window._IQ.raw));
+ window._IQ.questions = qs;
+ window._IQ.idx = 0; window._IQ.acertos = 0; window._IQ.erros = 0;
+ document.getElementById('inline-quiz-player').style.display = '';
+ _iqRender();
+ }
+
+ function _iqRender() {
+ const q = window._IQ.questions[window._IQ.idx];
+ if (!q) return;
+ window._IQ.answered = false; window._IQ.flipped = false;
+ const total = window._IQ.questions.length;
+ document.getElementById('iq-counter').textContent = (window._IQ.idx + 1) + ' de ' + total;
+ document.getElementById('iprog').style.width = ((window._IQ.idx + 1) / total * 100) + '%';
+ document.getElementById('ibtn-next').style.display = 'none';
+ const body = document.getElementById('iq-body');
+ if (q.type === 'mc') {
+ body.innerHTML = '<div class="mc-area fade"><p class="q-text">' + md(q.text) + '</p><div class="options">'
+ + q.options.map((o, i) => '<button class="opt" data-opt="' + esc(o) + '" data-answer="' + esc(q.answer) + '" onclick="iqSelectOpt(this)" aria-label="Opção ' + LETRAS[i] + ': ' + o + '"><span class="opt-letter">' + LETRAS[i] + '</span><span>' + md(o) + '</span></button>').join('')
+ + '</div><div class="feedback-line" id="iq-feedback" aria-live="polite"><span class="feedback-dot"></span><span id="iq-feedback-msg"></span></div>'
+ + (q.comment ? '<div class="comment-block" id="iq-comment"><div class="comment-label">comentário</div><div>' + md(q.comment) + '</div></div>' : '')
+ + '</div>';
+ } else {
+ body.innerHTML = '<div class="flashcard-area fade"><p class="q-text">' + md(q.text) + '</p>'
+ + '<div class="flashcard-simple" id="iq-flashcard"><button class="btn btn-reveal" onclick="iqFlip()">Revelar Resposta</button></div>'
+ + '<div class="feedback-line" id="iq-feedback" aria-live="polite"><span class="feedback-dot"></span><span id="iq-feedback-msg"></span></div>'
+ + (q.comment ? '<div class="comment-block" id="iq-comment"><div class="comment-label">comentário</div><div>' + md(q.comment) + '</div></div>' : '')
+ + '<div class="flashcard-btns" id="iq-fc-btns"><button class="btn btn-ok" onclick="iqMarkOk()">✓ Acertei</button><button class="btn btn-bad" onclick="iqMarkBad()">✗ Errei</button></div>'
+ + '</div>';
+ }
+ }
+
+ function iqSelectOpt(btn) {
+ if (window._IQ.answered) return;
+ window._IQ.answered = true;
+ const chosen = btn.dataset.opt, correct = btn.dataset.answer;
+ btn.closest('.options').querySelectorAll('.opt').forEach(b => {
+ b.disabled = true;
+ if (b.dataset.opt === correct) b.classList.add('correct');
+ else if (b === btn) b.classList.add('wrong');
+ else b.classList.add('revealed');
+ });
+ const ok = chosen === correct;
+ ok ? window._IQ.acertos++ : window._IQ.erros++;
+ _iqFeedback(ok, ok ? 'Correto' : 'Incorreto — a correta está destacada');
+ document.getElementById('iq-comment')?.classList.add('show');
+ document.getElementById('ibtn-next').style.display = '';
+ }
+
+ function iqFlip() {
+ const q = window._IQ.questions[window._IQ.idx];
+ const card = document.getElementById('iq-flashcard');
+ if (card) card.innerHTML = '<div class="ref-label">resposta de referência</div><div class="ref-content">' + md(q.reference) + '</div>';
+ document.getElementById('iq-fc-btns')?.style && (document.getElementById('iq-fc-btns').style.display = '');
+ }
+
+ function iqMarkOk() { _iqMark(true); }
+ function iqMarkBad() { _iqMark(false); }
+ function _iqMark(ok) {
+ if (window._IQ.answered) return;
+ window._IQ.answered = true;
+ ok ? window._IQ.acertos++ : window._IQ.erros++;
+ _iqFeedback(ok, ok ? 'Marcado como acertado' : 'Marcado como errado');
+ document.getElementById('iq-comment')?.classList.add('show');
+ const btns = document.getElementById('iq-fc-btns');
+ if (btns) btns.innerHTML = '';
+ document.getElementById('ibtn-next').style.display = '';
+ }
+
+ function _iqFeedback(ok, msg) {
+ const fb = document.getElementById('iq-feedback');
+ if (fb) { fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad'); }
+ const fm = document.getElementById('iq-feedback-msg');
+ if (fm) fm.textContent = msg;
+ }
+
+ function inlineNextQuestion() {
+ window._IQ.idx++;
+ if (window._IQ.idx >= window._IQ.questions.length) {
+ _iqShowResult();
+ } else {
+ _iqRender();
+ }
+ }
+
+ function _iqShowResult() {
+ document.getElementById('inline-quiz-player').style.display = 'none';
+ document.getElementById('inline-quiz-result').style.display = '';
+ const { acertos, erros, questions } = window._IQ;
+ const total = questions.length;
+ const pct = total ? Math.round(acertos / total * 100) : 0;
+ document.getElementById('ires-pct').textContent = pct + '%';
+ document.getElementById('ires-a').textContent = acertos;
+ document.getElementById('ires-t').textContent = total;
+ let nivel = pct >= 90 ? 'Excelente' : pct >= 70 ? 'Bom' : pct >= 50 ? 'Regular' : 'Insuficiente';
+ let det = pct >= 90 ? 'Domínio sólido deste assunto.' : pct >= 70 ? 'Boa base, revise os erros.' : pct >= 50 ? 'Lacunas importantes, releia o texto.' : 'Retome o conteúdo antes de prosseguir.';
+ document.getElementById('ires-msg').innerHTML = '<strong>' + nivel + '</strong> — ' + acertos + ' de ' + total + ' (' + pct + '%). ' + det;
+ }
+
+ function restartInlineQuiz() {
+ document.getElementById('inline-quiz-result').style.display = 'none';
+ window._IQ.idx = 0; window._IQ.acertos = 0; window._IQ.erros = 0;
+ window._IQ.questions = prepareQuizQuestions(parseQuizBody(window._IQ.raw));
+ document.getElementById('inline-quiz-player').style.display = '';
+ _iqRender();
+ }
+ </script>
+ {{ end }}
+
+ {{ if .Params.references }}
+ <div class="references">
+ <div class="ref-head">referências</div>
+ <div class="ref-list">
+ {{ range .Params.references }}<p>{{ . }}</p>{{ end }}
+ </div>
+ </div>
+ {{ end }}
+</div>
+
+{{ end }}
diff --git a/static/CNAME b/static/CNAME
new file mode 100644
index 0000000..488dfd6
--- /dev/null
+++ b/static/CNAME
@@ -0,0 +1 @@
+dir.sivaldodavi.com
diff --git a/static/dark-mode.js b/static/dark-mode.js
new file mode 100644
index 0000000..8e2d761
--- /dev/null
+++ b/static/dark-mode.js
@@ -0,0 +1,59 @@
+/**
+ * Dark Mode Toggle
+ * Respeita a preferência do sistema e permite alternar manualmente
+ * Salva a preferência no localStorage
+ */
+
+const DARK_MODE_KEY = 'caderno_dark_mode';
+
+function initDarkMode() {
+ // Verificar preferência salva
+ const saved = localStorage.getItem(DARK_MODE_KEY);
+ let isDark;
+
+ if (saved !== null) {
+ isDark = saved === 'true';
+ } else {
+ // Respeitar preferência do sistema
+ isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ }
+
+ applyDarkMode(isDark);
+}
+
+function applyDarkMode(isDark) {
+ const html = document.documentElement;
+ const toggle = document.getElementById('dark-mode-toggle');
+
+ if (isDark) {
+ html.setAttribute('data-theme', 'dark');
+ if (toggle) toggle.textContent = '☀️';
+ } else {
+ html.removeAttribute('data-theme');
+ if (toggle) toggle.textContent = '🌙';
+ }
+
+ localStorage.setItem(DARK_MODE_KEY, isDark);
+}
+
+function toggleDarkMode() {
+ const html = document.documentElement;
+ const isDark = html.getAttribute('data-theme') === 'dark';
+ applyDarkMode(!isDark);
+}
+
+// Inicializar quando o DOM estiver pronto
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initDarkMode);
+} else {
+ initDarkMode();
+}
+
+// Detectar mudanças na preferência do sistema
+window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
+ const saved = localStorage.getItem(DARK_MODE_KEY);
+ if (saved === null) {
+ // Se o usuário não escolheu manualmente, seguir o sistema
+ applyDarkMode(e.matches);
+ }
+});
diff --git a/static/dashboard.js b/static/dashboard.js
new file mode 100644
index 0000000..c14369f
--- /dev/null
+++ b/static/dashboard.js
@@ -0,0 +1,130 @@
+/**
+ * Dashboard de Progresso
+ * Lê dados do localStorage e mostra estatísticas de estudo
+ */
+
+var PROGRESS_PREFIX = window.PROGRESS_PREFIX || 'quiz_progress_';
+
+function initDashboard() {
+ const dashboard = document.getElementById('dashboard-stats');
+ if (!dashboard) return;
+
+ const stats = calculateStats();
+ renderDashboard(stats);
+}
+
+function calculateStats() {
+ const allKeys = Object.keys(localStorage);
+ const progressKeys = allKeys.filter((k) => k.startsWith(PROGRESS_PREFIX));
+
+ const stats = {
+ totalQuizzes: progressKeys.length,
+ totalAcertos: 0,
+ totalErros: 0,
+ disciplinas: {},
+ };
+
+ progressKeys.forEach((key) => {
+ try {
+ const data = JSON.parse(localStorage.getItem(key));
+ if (!data || !data.questions) return;
+
+ const disciplina = key.replace(PROGRESS_PREFIX, '').split('_')[0];
+ if (!stats.disciplinas[disciplina]) {
+ stats.disciplinas[disciplina] = {
+ acertos: 0,
+ erros: 0,
+ total: 0,
+ };
+ }
+
+ stats.disciplinas[disciplina].acertos += data.acertos || 0;
+ stats.disciplinas[disciplina].erros += data.erros || 0;
+ stats.disciplinas[disciplina].total += data.questions.length;
+
+ stats.totalAcertos += data.acertos || 0;
+ stats.totalErros += data.erros || 0;
+ } catch (e) {}
+ });
+
+ return stats;
+}
+
+function renderDashboard(stats) {
+ const dashboard = document.getElementById('dashboard-stats');
+ if (!dashboard) return;
+
+ const totalQuestoes = stats.totalAcertos + stats.totalErros;
+ const pctGeral = totalQuestoes > 0 ? Math.round((stats.totalAcertos / totalQuestoes) * 100) : 0;
+
+ let html = `
+ <div class="dashboard-container">
+ <h2>📊 Seu Progresso de Estudo</h2>
+
+ <div class="dashboard-overview">
+ <div class="stat-card">
+ <div class="stat-label">Quizzes Iniciados</div>
+ <div class="stat-value">${stats.totalQuizzes}</div>
+ </div>
+ <div class="stat-card">
+ <div class="stat-label">Taxa de Acerto Geral</div>
+ <div class="stat-value">${pctGeral}%</div>
+ </div>
+ <div class="stat-card">
+ <div class="stat-label">Questões Respondidas</div>
+ <div class="stat-value">${totalQuestoes}</div>
+ </div>
+ </div>
+
+ <div class="dashboard-disciplines">
+ <h3>Por Disciplina</h3>
+ `;
+
+ Object.entries(stats.disciplinas).forEach(([disc, data]) => {
+ const pct = data.total > 0 ? Math.round((data.acertos / data.total) * 100) : 0;
+ const nivel = pct >= 80 ? '🟢 Dominado' : pct >= 60 ? '🟡 Bom' : '🔴 Revisar';
+
+ html += `
+ <div class="disc-stat">
+ <div class="disc-name">${disc}</div>
+ <div class="disc-bar">
+ <div class="disc-progress" style="width: ${pct}%"></div>
+ </div>
+ <div class="disc-info">
+ <span>${data.acertos}/${data.total}</span>
+ <span>${pct}%</span>
+ <span>${nivel}</span>
+ </div>
+ </div>
+ `;
+ });
+
+ html += `
+ </div>
+ <div class="dashboard-actions">
+ <button class="btn" onclick="clearAllProgress()">Limpar Histórico</button>
+ </div>
+ </div>
+ `;
+
+ dashboard.innerHTML = html;
+}
+
+function clearAllProgress() {
+ if (confirm('Tem certeza que quer limpar todo o histórico de progresso?')) {
+ const allKeys = Object.keys(localStorage);
+ allKeys.forEach((key) => {
+ if (key.startsWith(PROGRESS_PREFIX)) {
+ localStorage.removeItem(key);
+ }
+ });
+ initDashboard();
+ }
+}
+
+// Inicializar quando o DOM estiver pronto
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initDashboard);
+} else {
+ initDashboard();
+}
diff --git a/static/footnotes.js b/static/footnotes.js
new file mode 100644
index 0000000..1d2700a
--- /dev/null
+++ b/static/footnotes.js
@@ -0,0 +1,77 @@
+/**
+ * Footnotes - Sistema de notas flutuantes (floater)
+ */
+
+let activeFloater = null;
+
+document.addEventListener('click', (e) => {
+ const fn = e.target.closest('a[href^="#fn:"]');
+ const close = e.target.closest('.f-close');
+
+ if (close) {
+ closeFloater();
+ return;
+ }
+
+ if (fn) {
+ e.preventDefault();
+ e.stopPropagation();
+ closeFloater();
+
+ const el = document.getElementById(fn.getAttribute('href').slice(1));
+ if (!el) return;
+
+ const clone = el.cloneNode(true);
+ clone.querySelector('.footnote-backref')?.remove();
+
+ const parent = fn.closest('p, li');
+ const quote = parent ? parent.innerText.slice(0, 40) + '…' : 'Nota';
+
+ openFloater(fn, quote, clone.innerHTML);
+ fn.classList.add('active');
+ } else if (activeFloater && !e.target.closest('.floater')) {
+ closeFloater();
+ }
+});
+
+function openFloater(target, quote, html) {
+ const f = document.createElement('div');
+ f.className = 'floater';
+ f.innerHTML = `<div class="f-label"><span>comentário</span><span class="f-close" role="button" tabindex="0" aria-label="Fechar nota">×</span></div><div class="f-quote">${quote}</div><div>${html}</div>`;
+
+ document.body.appendChild(f);
+
+ const r = target.getBoundingClientRect();
+ const top = window.scrollY + r.bottom + 8;
+ const padding = 20;
+ let left = window.scrollX + r.left;
+ const maxLeft = window.innerWidth - f.offsetWidth - padding;
+
+ if (left > maxLeft) left = Math.max(padding, maxLeft);
+
+ f.style.top = top + 'px';
+ f.style.left = left + 'px';
+
+ activeFloater = f;
+
+ // Permitir fechar com Enter ou Space
+ f.querySelector('.f-close').addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ closeFloater();
+ }
+ });
+}
+
+function closeFloater() {
+ if (activeFloater) {
+ activeFloater.remove();
+ activeFloater = null;
+ }
+ document.querySelectorAll('a[href^="#fn:"]').forEach((a) => a.classList.remove('active'));
+}
+
+// Fechar com Escape
+document.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape') closeFloater();
+});
diff --git a/static/home-filter.js b/static/home-filter.js
new file mode 100644
index 0000000..b2d1a47
--- /dev/null
+++ b/static/home-filter.js
@@ -0,0 +1,72 @@
+/**
+ * Home Filter — filtra disciplinas conforme você digita
+ * Só funciona quando há itens [data-filterable] na página
+ */
+document.addEventListener('DOMContentLoaded', () => {
+ const items = document.querySelectorAll('[data-filterable]');
+ if (!items.length) return;
+
+ // Cria campo de busca
+ const wrap = document.createElement('div');
+ wrap.className = 'filter-container';
+ wrap.innerHTML = `
+ <input
+ type="search"
+ id="home-filter"
+ class="filter-input"
+ placeholder="Buscar disciplina…"
+ aria-label="Filtrar disciplinas"
+ autocomplete="off"
+ spellcheck="false"
+ >
+ <span class="filter-count" id="filter-count" aria-live="polite"></span>
+ `;
+
+ // Insere antes do disc-grid dentro do <main>
+ const grid = document.querySelector('.disc-grid');
+ if (grid) {
+ grid.parentNode.insertBefore(wrap, grid);
+ } else {
+ const main = document.querySelector('main');
+ if (main) main.insertBefore(wrap, main.firstChild);
+ }
+
+ const input = document.getElementById('home-filter');
+ const count = document.getElementById('filter-count');
+
+ // Normaliza texto para comparação (remove acentos, maiúsculas)
+ function normalize(str) {
+ return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
+ }
+
+ input.addEventListener('input', () => {
+ const q = normalize(input.value.trim());
+ let visible = 0;
+
+ items.forEach(item => {
+ const text = normalize(item.textContent);
+ const match = !q || text.includes(q);
+ item.style.display = match ? '' : 'none';
+ if (match) visible++;
+ });
+
+ count.textContent = q ? `${visible} resultado${visible !== 1 ? 's' : ''}` : '';
+ });
+
+ // Esc limpa e desfoca
+ input.addEventListener('keydown', e => {
+ if (e.key === 'Escape') {
+ input.value = '';
+ input.dispatchEvent(new Event('input'));
+ input.blur();
+ }
+ });
+
+ // Foco automático na home com / ou Ctrl+K
+ document.addEventListener('keydown', e => {
+ if ((e.key === '/' || (e.ctrlKey && e.key === 'k')) && document.activeElement !== input) {
+ e.preventDefault();
+ input.focus();
+ }
+ });
+});
diff --git a/static/keyboard-shortcuts.js b/static/keyboard-shortcuts.js
new file mode 100644
index 0000000..53cfe44
--- /dev/null
+++ b/static/keyboard-shortcuts.js
@@ -0,0 +1,73 @@
+/**
+ * Keyboard Shortcuts
+ * Atalhos para acelerar a navegação e resposta de quizzes
+ *
+ * Atalhos:
+ * 1-5: Selecionar opção A-E
+ * Espaço: Próxima questão
+ * Esc: Fechar notas/floaters
+ * ?: Mostrar ajuda
+ */
+
+const SHORTCUTS = {
+ '1': () => selectOptionByLetter(0),
+ '2': () => selectOptionByLetter(1),
+ '3': () => selectOptionByLetter(2),
+ '4': () => selectOptionByLetter(3),
+ '5': () => selectOptionByLetter(4),
+ ' ': (e) => {
+ e.preventDefault();
+ const nextBtn = document.getElementById('btn-next');
+ if (nextBtn && nextBtn.style.display !== 'none') {
+ nextBtn.click();
+ }
+ },
+ 'Escape': () => {
+ // Fechar floaters se existir
+ if (typeof closeFloater === 'function') {
+ closeFloater();
+ }
+ },
+ '?': (e) => {
+ e.preventDefault();
+ showKeyboardHelp();
+ },
+};
+
+function selectOptionByLetter(index) {
+ const buttons = document.querySelectorAll('.opt');
+ if (buttons[index]) {
+ buttons[index].click();
+ }
+}
+
+function showKeyboardHelp() {
+ const help = `
+⌨️ Atalhos de Teclado
+
+Quizzes:
+ 1-5 Selecionar opção A-E
+ SPC Próxima questão
+ ESC Fechar notas
+
+Geral:
+ ESC Fechar floaters
+ ? Mostrar esta ajuda
+ `;
+
+ alert(help);
+}
+
+document.addEventListener('keydown', (e) => {
+ // Ignorar se está digitando em um input/textarea
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
+ return;
+ }
+
+ const key = e.key;
+ const handler = SHORTCUTS[key];
+
+ if (handler) {
+ handler(e);
+ }
+});
diff --git a/static/main.css b/static/main.css
new file mode 100644
index 0000000..d4257a4
--- /dev/null
+++ b/static/main.css
@@ -0,0 +1,1560 @@
+/* ── VARIÁVEIS E RESET ── */
+:root {
+ --serif: 'Lora', serif;
+ --mono: 'JetBrains Mono', monospace;
+ --text: #dedad4;
+ --text-2: #a8a8a8;
+ --muted: #6a6a75;
+ --surface: #1a1a1f;
+ --border: #2a2a30;
+ --border-2: #3a3a40;
+ --amber: #c9a961;
+ --t: 150ms ease;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ background: #0f0f13;
+ color: var(--text);
+ font-family: var(--serif);
+ font-size: 16px;
+ line-height: 1.6;
+}
+
+body {
+ background: #0f0f13;
+ color: var(--text);
+ font-family: var(--serif);
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+/* ── HEADER ── */
+header {
+ padding: 1.2rem 2rem;
+ border-bottom: 1px solid var(--border);
+ background: rgba(15, 15, 19, 0.95);
+ backdrop-filter: blur(4px);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+}
+
+.site-name {
+ font-family: var(--mono);
+ font-size: 0.75rem;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ color: var(--text);
+ text-decoration: none;
+ transition: color var(--t);
+}
+
+.site-name:hover {
+ color: var(--amber);
+}
+
+.header-sep {
+ color: var(--border-2);
+ margin: 0 0.5rem;
+}
+
+.breadcrumb {
+ color: var(--text-2);
+ font-size: 0.9rem;
+}
+
+.breadcrumb-link {
+ color: var(--text-2);
+ text-decoration: none;
+ transition: color var(--t);
+}
+
+.breadcrumb-link:hover {
+ color: var(--text);
+}
+
+/* ── MAIN ── */
+main {
+ flex: 1;
+ padding: 2rem;
+ max-width: 900px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+/* ── FADE ANIMATION ── */
+.fade {
+ animation: fadeIn 200ms ease;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.fade-out {
+ animation: fadeOut 100ms ease;
+}
+
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+/* ── GRID DE DISCIPLINAS ── */
+.disc-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 1.2rem;
+ margin-bottom: 2rem;
+}
+
+.disc-card {
+ display: flex;
+ flex-direction: column;
+ padding: 1.2rem;
+ border: 1px dashed var(--border);
+ border-radius: 4px;
+ text-decoration: none;
+ color: var(--text);
+ transition: all var(--t);
+ cursor: pointer;
+}
+
+.disc-card:hover {
+ border-color: var(--amber);
+ background: color-mix(in srgb, var(--amber) 3%, transparent);
+ transform: translateY(-2px);
+}
+
+.disc-card-name {
+ font-size: 1rem;
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+ line-height: 1.3;
+}
+
+.disc-card-meta {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+/* ── BOTÃO VOLTAR ── */
+.disc-back {
+ margin-bottom: 1.2rem;
+}
+
+.disc-back a {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ color: var(--muted);
+ text-decoration: none;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ transition: color var(--t);
+}
+
+.disc-back a:hover {
+ color: var(--text-2);
+}
+
+/* ── DISCIPLINA ── */
+.discipline {
+ margin-bottom: 2.5rem;
+}
+
+.discipline-name {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.65rem;
+ padding-bottom: 0.45rem;
+ border-bottom: 1px solid var(--border);
+}
+
+/* ── INTRO/NOTAS DA DISCIPLINA ── */
+.disc-intro {
+ margin: 1.2rem 0 1.8rem;
+ padding: 1rem 1.2rem;
+ border-left: 2px solid var(--amber);
+ background: color-mix(in srgb, var(--amber) 5%, transparent);
+ border-radius: 0 4px 4px 0;
+}
+
+.disc-intro-prose {
+ font-size: 0.9rem;
+ color: var(--text-2);
+ margin: 0;
+}
+
+.disc-intro-prose p:first-child {
+ margin-top: 0;
+}
+
+.disc-intro-prose p:last-child {
+ margin-bottom: 0;
+}
+
+.disc-intro-refs {
+ margin-top: 0.75rem;
+ padding-top: 0.5rem;
+ border-top: 1px solid var(--border);
+}
+
+/* ── TÓPICOS (SUBPASTAS) ── */
+.topic-section {
+ margin-bottom: 0.5rem;
+}
+
+.topic-head {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ margin: 1.4rem 0 0.3rem;
+ padding-bottom: 0.3rem;
+ border-bottom: 1px dashed var(--border);
+}
+
+.subject-group {
+ padding: 0.85rem 0;
+ border-bottom: 1px solid var(--border);
+}
+
+.subject-group:last-child {
+ border-bottom: none;
+}
+
+.subject-head {
+ font-size: 0.95rem;
+ color: var(--text);
+ margin-bottom: 0.5rem;
+}
+
+.item-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ padding: 0.35rem 0;
+ cursor: pointer;
+ text-decoration: none;
+ color: var(--text-2);
+ transition: opacity var(--t);
+ gap: 1rem;
+}
+
+.item-row:hover {
+ opacity: 0.55;
+ color: var(--text);
+}
+
+.item-kind {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--muted);
+ min-width: 3.2rem;
+ flex-shrink: 0;
+}
+
+.item-kind.txt {
+ color: var(--amber);
+}
+
+.item-kind.qz {
+ color: var(--text-2);
+}
+
+.item-title {
+ flex: 1;
+ font-size: 0.9rem;
+}
+
+.item-meta {
+ font-family: var(--mono);
+ font-size: 0.66rem;
+ color: var(--muted);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+/* ── LEITOR DE TEXTOS & PROSE ── */
+.text-meta {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ color: var(--muted);
+ margin-bottom: 1.4rem;
+ letter-spacing: 0.02em;
+ display: flex;
+ gap: 0.6rem;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.text-meta .dot {
+ width: 3px;
+ height: 3px;
+ border-radius: 50%;
+ background: var(--border-2);
+}
+
+.text-title {
+ font-size: 1.4rem;
+ line-height: 1.35;
+ margin-bottom: 1.6rem;
+ color: var(--text);
+ font-weight: 600;
+}
+
+.prose {
+ font-size: 1rem;
+ line-height: 1.8;
+ color: var(--text);
+}
+
+.prose p {
+ margin-bottom: 1.1rem;
+}
+
+.prose h2 {
+ font-family: var(--serif);
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin: 1.8rem 0 0.8rem;
+ color: var(--text);
+}
+
+.prose h3 {
+ font-family: var(--mono);
+ font-size: 0.72rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--muted);
+ margin: 1.6rem 0 0.6rem;
+}
+
+.prose blockquote {
+ border-left: 2px solid var(--border-2);
+ padding: 0.2rem 0 0.2rem 1rem;
+ margin: 1rem 0;
+ color: var(--text-2);
+ font-style: italic;
+}
+
+.prose em {
+ color: var(--text-2);
+}
+
+.prose strong {
+ color: var(--text);
+ font-weight: 600;
+}
+
+.prose code {
+ font-family: var(--mono);
+ font-size: 0.85em;
+ background: var(--surface);
+ padding: 0.05em 0.35em;
+ border-radius: 2px;
+ color: var(--text-2);
+}
+
+/* ── TABELAS ── */
+.prose table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1.8rem 0;
+ font-size: 0.9rem;
+ color: var(--text-2);
+}
+
+.prose th {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ color: var(--muted);
+ border-bottom: 1px solid var(--border-2);
+ padding: 0.5rem 0.6rem;
+ text-align: left;
+}
+
+.prose td {
+ padding: 0.6rem;
+ border-bottom: 1px solid var(--border);
+ vertical-align: top;
+}
+
+.prose tr:hover td {
+ color: var(--text);
+}
+
+@media (max-width: 480px) {
+ .prose table {
+ overflow-x: auto;
+ display: block;
+ }
+}
+
+/* ── NOTAS DE RODAPÉ ── */
+a[href^="#fn:"] {
+ text-decoration: none;
+ color: var(--amber);
+ cursor: pointer;
+ transition: opacity var(--t);
+}
+
+a[href^="#fn:"]:hover {
+ opacity: 0.7;
+}
+
+a[href^="#fn:"].active {
+ font-weight: 600;
+}
+
+.footnotes {
+ margin-top: 2rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border);
+ font-size: 0.9rem;
+}
+
+.footnote-backref {
+ text-decoration: none;
+ color: var(--amber);
+}
+
+/* ── FLOATER (POPUP DE NOTAS) ── */
+.floater {
+ position: fixed;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 1rem;
+ max-width: 320px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ animation: slideUp 150ms ease;
+}
+
+@keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.f-label {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--border);
+}
+
+.f-close {
+ cursor: pointer;
+ font-size: 1.2rem;
+ color: var(--text-2);
+ transition: color var(--t);
+}
+
+.f-close:hover {
+ color: var(--text);
+}
+
+.f-quote {
+ font-size: 0.85rem;
+ color: var(--text-2);
+ font-style: italic;
+ margin-bottom: 0.5rem;
+}
+
+/* ── SUMÁRIO (TOC) ── */
+.toc {
+ margin: 1.5rem 0 2rem;
+ padding: 1rem;
+ background: color-mix(in srgb, var(--amber) 3%, transparent);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+}
+
+.toc-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.75rem;
+}
+
+#toc-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.toc-h2 {
+ margin-bottom: 0.5rem;
+}
+
+.toc-h2 a {
+ font-size: 0.95rem;
+ color: var(--text);
+ text-decoration: none;
+ transition: color var(--t);
+}
+
+.toc-h2 a:hover {
+ color: var(--amber);
+}
+
+.toc-h3 {
+ margin-left: 1.2rem;
+ margin-bottom: 0.3rem;
+}
+
+.toc-h3 a {
+ font-size: 0.85rem;
+ color: var(--text-2);
+ text-decoration: none;
+ transition: color var(--t);
+}
+
+.toc-h3 a:hover {
+ color: var(--text);
+}
+
+/* ── REFERÊNCIAS ── */
+.references {
+ margin-top: 2rem;
+ padding-top: 1.5rem;
+ border-top: 1px solid var(--border);
+}
+
+.ref-head {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 1rem;
+}
+
+.ref-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.ref-list p {
+ font-size: 0.9rem;
+ color: var(--text-2);
+ margin: 0;
+ line-height: 1.5;
+}
+
+/* ── QUIZ: LAYOUT GERAL ── */
+.progress-wrap {
+ width: 100%;
+ height: 3px;
+ background: var(--border);
+ border-radius: 2px;
+ overflow: hidden;
+ margin-bottom: 1.5rem;
+}
+
+.progress-fill {
+ height: 100%;
+ background: var(--amber);
+ width: 0%;
+ transition: width 300ms ease;
+}
+
+.q-meta {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ color: var(--muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ margin-bottom: 1.2rem;
+}
+
+#quiz-body {
+ margin-bottom: 1.5rem;
+}
+
+/* ── QUIZ: MÚLTIPLA ESCOLHA ── */
+.q-text {
+ font-size: 1rem;
+ line-height: 1.8;
+ color: var(--text);
+ margin-bottom: 1.5rem;
+}
+
+.options {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ margin-bottom: 1.5rem;
+}
+
+.opt {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.75rem;
+ padding: 0.75rem 1rem;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all var(--t);
+ font-family: var(--serif);
+ font-size: 0.95rem;
+ color: var(--text);
+ text-align: left;
+ min-height: 3rem;
+}
+
+.opt:hover:not(:disabled) {
+ border-color: var(--amber);
+ background: color-mix(in srgb, var(--amber) 5%, transparent);
+}
+
+.opt:disabled {
+ cursor: not-allowed;
+ opacity: 0.7;
+}
+
+.opt.correct {
+ border-color: #4ade80;
+ background: color-mix(in srgb, #4ade80 15%, transparent);
+}
+
+.opt.wrong {
+ border-color: #ef4444;
+ background: color-mix(in srgb, #ef4444 15%, transparent);
+}
+
+.opt.revealed {
+ opacity: 0.5;
+}
+
+.opt-letter {
+ font-family: var(--mono);
+ font-size: 0.75rem;
+ font-weight: 600;
+ color: var(--muted);
+ min-width: 1.5rem;
+ flex-shrink: 0;
+}
+
+/* ── QUIZ: RESPOSTA ABERTA ── */
+.open-area {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+textarea {
+ padding: 1rem;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ color: var(--text);
+ font-family: var(--serif);
+ font-size: 0.95rem;
+ line-height: 1.6;
+ resize: vertical;
+ min-height: 120px;
+ transition: border-color var(--t);
+}
+
+textarea:focus {
+ outline: none;
+ border-color: var(--amber);
+}
+
+textarea::placeholder {
+ color: var(--muted);
+}
+
+/* ── QUIZ: FEEDBACK ── */
+.feedback-line {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-family: var(--mono);
+ font-size: 0.75rem;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ color: var(--text-2);
+ opacity: 0;
+ transition: opacity var(--t);
+}
+
+.feedback-line.show {
+ opacity: 1;
+}
+
+.feedback-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--text-2);
+}
+
+.feedback-line.ok .feedback-dot {
+ background: #4ade80;
+}
+
+.feedback-line.bad .feedback-dot {
+ background: #ef4444;
+}
+
+/* ── QUIZ: COMENTÁRIO ── */
+.comment-block {
+ padding: 1rem;
+ background: color-mix(in srgb, var(--amber) 5%, transparent);
+ border-left: 2px solid var(--amber);
+ border-radius: 0 4px 4px 0;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height var(--t);
+}
+
+.comment-block.show {
+ max-height: 500px;
+}
+
+.comment-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+}
+
+.comment-block > div:last-child {
+ font-size: 0.9rem;
+ color: var(--text-2);
+ line-height: 1.6;
+}
+
+/* ── QUIZ: REFERÊNCIA (RESPOSTA ABERTA) ── */
+.ref-block {
+ padding: 1rem;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height var(--t));
+}
+
+.ref-block.show {
+ max-height: 500px;
+}
+
+.ref-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+}
+
+.ref-block > div:last-child {
+ font-size: 0.9rem;
+ color: var(--text-2);
+ line-height: 1.6;
+}
+
+/* ── QUIZ: BOTÕES ── */
+.next-row {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 1.5rem;
+}
+
+.open-btns {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: flex-end;
+}
+
+.btn {
+ padding: 0.6rem 1.2rem;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ color: var(--text);
+ font-family: var(--serif);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: all var(--t);
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.btn:hover:not(:disabled) {
+ border-color: var(--amber);
+ background: color-mix(in srgb, var(--amber) 10%, transparent);
+}
+
+.btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.btn-ok {
+ border-color: #4ade80;
+ color: #4ade80;
+}
+
+.btn-ok:hover:not(:disabled) {
+ background: color-mix(in srgb, #4ade80 15%, transparent);
+}
+
+.btn-bad {
+ border-color: #ef4444;
+ color: #ef4444;
+}
+
+.btn-bad:hover:not(:disabled) {
+ background: color-mix(in srgb, #ef4444 15%, transparent);
+}
+
+/* ── QUIZ: RESULTADO ── */
+.res-score {
+ font-size: 3rem;
+ font-weight: 600;
+ color: var(--amber);
+ text-align: center;
+ margin-bottom: 0.5rem;
+}
+
+.res-subtitle {
+ font-size: 1rem;
+ color: var(--text-2);
+ text-align: center;
+ margin-bottom: 1.5rem;
+}
+
+.res-divider {
+ height: 1px;
+ background: var(--border);
+ margin: 1.5rem 0;
+}
+
+.res-stats {
+ display: flex;
+ justify-content: center;
+ gap: 3rem;
+ margin-bottom: 1.5rem;
+}
+
+.rs-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.rs-num {
+ font-size: 1.8rem;
+ font-weight: 600;
+}
+
+.rs-num.g {
+ color: #4ade80;
+}
+
+.rs-num.r {
+ color: #ef4444;
+}
+
+.rs-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.res-msg {
+ padding: 1rem;
+ background: color-mix(in srgb, var(--amber) 5%, transparent);
+ border-left: 2px solid var(--amber);
+ border-radius: 0 4px 4px 0;
+ margin-bottom: 1.5rem;
+}
+
+.res-msg-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
+}
+
+.res-msg-body {
+ font-size: 0.95rem;
+ color: var(--text-2);
+ line-height: 1.6;
+}
+
+.res-btns {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* ── EXTRAS DE DISCIPLINA (COLAPSÁVEIS) ── */
+.disc-extras-wrap {
+ margin: 1.5rem 0 2rem;
+}
+
+.disc-extra {
+ margin-bottom: 0.75rem;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.disc-extra-summary {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.75rem 1rem;
+ background: var(--surface);
+ cursor: pointer;
+ user-select: none;
+ transition: background var(--t);
+}
+
+.disc-extra-summary:hover {
+ background: color-mix(in srgb, var(--amber) 5%, transparent);
+}
+
+.disc-extra-label {
+ font-family: var(--mono);
+ font-size: 0.68rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.disc-extra-count {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--text-2);
+ letter-spacing: 0.05em;
+}
+
+.disc-extra-chevron {
+ color: var(--text-2);
+ transition: transform var(--t);
+}
+
+.disc-extra[open] .disc-extra-chevron {
+ transform: rotate(180deg);
+}
+
+.disc-extra-body {
+ padding: 1rem;
+ border-top: 1px solid var(--border);
+}
+
+.disc-extra-row {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.5rem 0;
+ text-decoration: none;
+ color: var(--text-2);
+ transition: color var(--t);
+}
+
+.disc-extra-row:hover {
+ color: var(--text);
+}
+
+.disc-extra-icon {
+ font-size: 0.8rem;
+ color: var(--muted);
+ min-width: 1.2rem;
+}
+
+.disc-extra-name {
+ flex: 1;
+ font-size: 0.9rem;
+}
+
+.disc-extra-meta {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+
+.disc-extra-prose {
+ font-size: 0.9rem;
+ color: var(--text-2);
+}
+
+.disc-extra-prose p {
+ margin-bottom: 0.75rem;
+}
+
+.disc-extra-prose p:last-child {
+ margin-bottom: 0;
+}
+
+.disc-extra-kind-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin: 1rem 0 0.5rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px dashed var(--border);
+}
+
+.disc-extra-kind-label:first-child {
+ margin-top: 0;
+}
+
+/* ── SUBJECT QUIZZES (LINKS PARA EXERCÍCIOS) ── */
+.subject-quizzes {
+ margin: 1.5rem 0 2rem;
+ padding: 1rem;
+ background: color-mix(in srgb, var(--amber) 3%, transparent);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+}
+
+.sq-label {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 0.75rem;
+}
+
+.sq-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.sq-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.6rem 0.75rem;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 3px;
+ text-decoration: none;
+ color: var(--text);
+ transition: all var(--t);
+}
+
+.sq-item:hover {
+ border-color: var(--amber);
+ background: color-mix(in srgb, var(--amber) 8%, transparent);
+}
+
+.sq-name {
+ font-size: 0.9rem;
+ flex: 1;
+}
+
+.sq-meta {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ flex-shrink: 0;
+}
+
+/* ── SUBJECT LIST ── */
+.subject-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.subject-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem 0;
+ border-bottom: 1px solid var(--border);
+ text-decoration: none;
+ color: var(--text);
+ transition: all var(--t);
+}
+
+.subject-row:last-child {
+ border-bottom: none;
+}
+
+.subject-row:hover {
+ color: var(--amber);
+ padding-left: 0.5rem;
+}
+
+.subject-row-name {
+ font-size: 0.95rem;
+ flex: 1;
+}
+
+.subject-row-meta {
+ font-family: var(--mono);
+ font-size: 0.62rem;
+ color: var(--muted);
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ flex-shrink: 0;
+}
+
+.empty-note {
+ font-size: 0.9rem;
+ color: var(--muted);
+ font-style: italic;
+ padding: 1rem 0;
+}
+
+/* ── RESPONSIVIDADE ── */
+@media (max-width: 768px) {
+ main {
+ padding: 1.5rem;
+ }
+
+ .disc-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .res-stats {
+ gap: 2rem;
+ }
+
+ header {
+ padding: 1rem 1.5rem;
+ }
+}
+
+@media (max-width: 480px) {
+ main {
+ padding: 1rem;
+ }
+
+ .text-title {
+ font-size: 1.2rem;
+ }
+
+ .prose {
+ font-size: 0.95rem;
+ }
+
+ .options {
+ gap: 0.5rem;
+ }
+
+ .opt {
+ padding: 0.6rem 0.75rem;
+ min-height: 2.5rem;
+ }
+
+ .floater {
+ max-width: 280px;
+ }
+
+ .res-score {
+ font-size: 2.5rem;
+ }
+
+ .res-stats {
+ gap: 1.5rem;
+ }
+}
+
+/* ── FLASHCARD ── */
+.flashcard-area {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.flashcard {
+ perspective: 1000px;
+ cursor: pointer;
+ height: 300px;
+ position: relative;
+}
+
+.flashcard-front,
+.flashcard-back {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ backface-visibility: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 2px solid var(--amber);
+ border-radius: 4px;
+ padding: 2rem;
+ text-align: center;
+ transition: transform 0.6s;
+}
+
+.flashcard-front {
+ background: var(--surface);
+ color: var(--text);
+ transform: rotateY(0deg);
+}
+
+.flashcard-back {
+ background: var(--amber);
+ color: #0f0f13;
+ transform: rotateY(180deg);
+}
+
+.flashcard.flipped .flashcard-front {
+ transform: rotateY(180deg);
+}
+
+.flashcard.flipped .flashcard-back {
+ transform: rotateY(0deg);
+}
+
+.flashcard-hint {
+ font-size: 1.2rem;
+ opacity: 0.7;
+}
+
+.flashcard-btns {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+}
+
+/* ── FILTRO NA HOME ── */
+.filter-container {
+ margin-bottom: 2rem;
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+
+.filter-input {
+ flex: 1;
+ padding: 0.75rem 1rem;
+ background: var(--surface);
+ color: var(--text);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ font-size: 1rem;
+ font-family: var(--serif);
+}
+
+.filter-input:focus {
+ outline: none;
+ border-color: var(--amber);
+}
+
+.filter-count {
+ color: var(--amber);
+ font-size: 0.9rem;
+ white-space: nowrap;
+}
+
+/* ── DASHBOARD ── */
+.dashboard-container {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+}
+
+.dashboard-overview {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+.stat-card {
+ background: #0f0f13;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ padding: 1.5rem;
+ text-align: center;
+}
+
+.stat-label {
+ font-size: 0.9rem;
+ color: var(--muted);
+ margin-bottom: 0.5rem;
+}
+
+.stat-value {
+ font-size: 2rem;
+ font-weight: 600;
+ color: var(--amber);
+}
+
+.dashboard-disciplines {
+ margin-top: 2rem;
+}
+
+.disc-stat {
+ margin-bottom: 1.5rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid var(--border);
+}
+
+.disc-name {
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+}
+
+.disc-bar {
+ height: 8px;
+ background: #0f0f13;
+ border-radius: 4px;
+ overflow: hidden;
+ margin-bottom: 0.5rem;
+}
+
+.disc-progress {
+ height: 100%;
+ background: #4ade80;
+ transition: width 0.3s;
+}
+
+.disc-info {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.85rem;
+ color: var(--muted);
+}
+
+.dashboard-actions {
+ margin-top: 2rem;
+ text-align: center;
+}
+
+/* ── MODO ESCURO TOGGLE ── */
+#dark-mode-toggle {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ margin-left: auto;
+ padding: 0.5rem;
+ border-radius: 4px;
+ transition: background-color var(--t);
+}
+
+#dark-mode-toggle:hover {
+ background-color: var(--border);
+}
+
+/* ── PRINT-FRIENDLY ── */
+@media print {
+ header,
+ .filter-container,
+ .dashboard-container,
+ .btn,
+ .next-row,
+ #dark-mode-toggle,
+ .flashcard-btns,
+ .open-btns {
+ display: none;
+ }
+
+ body {
+ background: white;
+ color: black;
+ padding: 0;
+ }
+
+ main {
+ max-width: 100%;
+ padding: 0;
+ }
+
+ .q-text,
+ .flashcard-back {
+ page-break-inside: avoid;
+ }
+
+ .flashcard {
+ border: 1px solid black;
+ background: white;
+ color: black;
+ }
+
+ .flashcard-front,
+ .flashcard-back {
+ position: static;
+ transform: none;
+ border: none;
+ background: white;
+ color: black;
+ }
+}
+
+/* ── RESPONSIVIDADE MOBILE ── */
+@media (max-width: 768px) {
+ .dashboard-overview {
+ grid-template-columns: 1fr;
+ }
+
+ .flashcard {
+ height: 250px;
+ }
+}
+
+@media (max-width: 480px) {
+ .flashcard {
+ height: 200px;
+ }
+
+ .filter-container {
+ flex-direction: column;
+ }
+
+ .filter-input {
+ width: 100%;
+ }
+}
+
+/* ── QUIZ INLINE (dentro do assunto) ── */
+.inline-quiz-wrap {
+ margin: 2.5rem 0 1.5rem;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.inline-quiz-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.9rem 1.2rem;
+ background: var(--surface);
+ border-bottom: 1px solid var(--border);
+}
+
+.inline-quiz-head .sq-label {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+.inline-quiz-link {
+ font-family: var(--mono);
+ font-size: 0.65rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--muted);
+ text-decoration: none;
+ transition: color var(--t);
+}
+
+.inline-quiz-link:hover {
+ color: var(--amber);
+}
+
+#inline-quiz-body,
+#inline-quiz-player,
+#inline-quiz-result {
+ padding: 1.2rem;
+}
+
+.btn-start-quiz {
+ width: 100%;
+ padding: 0.85rem;
+ justify-content: center;
+ background: color-mix(in srgb, var(--amber) 8%, transparent);
+ border-color: var(--amber);
+ color: var(--amber);
+}
+
+.btn-start-quiz:hover {
+ background: color-mix(in srgb, var(--amber) 15%, transparent);
+}
+
+/* ── TABELA NA PROSE ── */
+.prose table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.88rem;
+ margin: 1.5rem 0;
+}
+
+.prose th {
+ font-family: var(--mono);
+ font-size: 0.7rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--muted);
+ padding: 0.55rem 0.75rem;
+ border-bottom: 1px solid var(--border-2);
+ text-align: left;
+}
+
+.prose td {
+ padding: 0.55rem 0.75rem;
+ border-bottom: 1px solid var(--border);
+ color: var(--text-2);
+ vertical-align: top;
+}
+
+.prose tr:last-child td {
+ border-bottom: none;
+}
+
+.prose tr:hover td {
+ background: color-mix(in srgb, var(--amber) 3%, transparent);
+}
diff --git a/static/manifest.json b/static/manifest.json
new file mode 100644
index 0000000..6460cbe
--- /dev/null
+++ b/static/manifest.json
@@ -0,0 +1,34 @@
+{
+ "name": "Caderno de Estudos - Direito",
+ "short_name": "Caderno",
+ "description": "Caderno interativo de estudos com quizzes e simulados de Direito",
+ "start_url": "/",
+ "scope": "/",
+ "display": "standalone",
+ "background_color": "#0f0f13",
+ "theme_color": "#c9a961",
+ "orientation": "portrait-primary",
+ "icons": [
+ {
+ "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%230f0f13' width='192' height='192'/><text x='96' y='120' font-size='80' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>",
+ "sizes": "192x192",
+ "type": "image/svg+xml",
+ "purpose": "any"
+ },
+ {
+ "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%230f0f13' width='192' height='192'/><text x='96' y='120' font-size='80' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>",
+ "sizes": "512x512",
+ "type": "image/svg+xml",
+ "purpose": "maskable"
+ }
+ ],
+ "categories": ["education", "productivity"],
+ "screenshots": [
+ {
+ "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 540 720'><rect fill='%230f0f13' width='540' height='720'/><text x='270' y='360' font-size='60' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>Caderno</text></svg>",
+ "sizes": "540x720",
+ "type": "image/svg+xml",
+ "form_factor": "narrow"
+ }
+ ]
+}
diff --git a/static/offline.html b/static/offline.html
new file mode 100644
index 0000000..0cb1bdc
--- /dev/null
+++ b/static/offline.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="pt-BR">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Sem conexão · Caderno</title>
+<style>
+ body { background:#0f0f13; color:#dedad4; font-family:'Georgia',serif; display:flex;
+ align-items:center; justify-content:center; min-height:100vh; margin:0; text-align:center; padding:2rem; }
+ h1 { font-size:1.2rem; margin-bottom:.75rem; }
+ p { font-size:.9rem; color:#6a6a75; }
+ a { color:#c9a961; }
+</style>
+</head>
+<body>
+ <div>
+ <h1>Você está offline</h1>
+ <p>Páginas já visitadas estão disponíveis em cache.<br>
+ <a href="/">Voltar ao início</a></p>
+ </div>
+</body>
+</html>
diff --git a/static/quiz-engine.js b/static/quiz-engine.js
new file mode 100644
index 0000000..03e50a5
--- /dev/null
+++ b/static/quiz-engine.js
@@ -0,0 +1,170 @@
+/**
+ * Quiz Engine - Renderiza questões na página
+ */
+
+function _renderQuestion(q) {
+ if (!q) return;
+ const body = document.getElementById('quiz-body');
+ if (!body) return;
+
+ const counter = document.getElementById('q-counter');
+ if (counter) {
+ counter.textContent = `${_qState.qIndex + 1} de ${_qState.questions.length}`;
+ }
+
+ const prog = document.getElementById('prog');
+ if (prog) {
+ const pct = ((_qState.qIndex + 1) / _qState.questions.length) * 100;
+ prog.style.width = pct + '%';
+ }
+
+ if (q.type === 'mc') {
+ body.innerHTML = `<div class="mc-area fade">
+ <p class="q-text">${md(q.text)}</p>
+ <div class="options">
+ ${q.options
+ .map(
+ (o, i) => `<button class="opt" data-opt="${esc(o)}" data-answer="${esc(q.answer)}" onclick="selectOpt(this)" aria-label="Opção ${LETRAS[i]}: ${o}">
+ <span class="opt-letter">${LETRAS[i]}</span>
+ <span>${md(o)}</span>
+ </button>`
+ )
+ .join('')}
+ </div>
+ <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div>
+ ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''}
+ </div>`;
+ } else {
+ // Modo Flashcard para questões abertas
+ const isRevealed = _qState.flipped || false;
+ body.innerHTML = `<div class="flashcard-area fade">
+ <p class="q-text">${md(q.text)}</p>
+ <div class="flashcard-simple" id="flashcard">
+ ${!isRevealed ? `<button class="btn btn-reveal" onclick="flipFlashcard()">Revelar Resposta</button>` : `<div class="ref-label">resposta de referência</div><div class="ref-content">${md(q.reference)}</div>`}
+ </div>
+ <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div>
+ ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''}
+ <div class="flashcard-btns" id="flashcard-btns">
+ <button class="btn btn-ok" onclick="markFlashcardOk()">✓ Acertei</button>
+ <button class="btn btn-bad" onclick="markFlashcardBad()">✗ Errei</button>
+ </div>
+ </div>`;
+ }
+}
+
+function selectOpt(btn) {
+ if (_qState.answered) return;
+ _qState.answered = true;
+ const chosen = btn.dataset.opt;
+ const correct = btn.dataset.answer;
+ btn.closest('.options').querySelectorAll('.opt').forEach((b) => {
+ b.disabled = true;
+ if (b.dataset.opt === correct) b.classList.add('correct');
+ else if (b === btn) b.classList.add('wrong');
+ else b.classList.add('revealed');
+ });
+ if (chosen === correct) {
+ _qState.acertos++;
+ setFeedback(true, 'Correto');
+ } else {
+ _qState.erros++;
+ setFeedback(false, 'Incorreto — a correta está destacada');
+ }
+ document.getElementById('comment')?.classList.add('show');
+ document.getElementById('btn-next').style.display = '';
+}
+
+function flipFlashcard() {
+ const card = document.getElementById('flashcard');
+ if (card) {
+ _qState.flipped = true;
+ _renderQuestion(_qState.questions[_qState.qIndex]);
+ }
+}
+
+function markFlashcardOk() {
+ _qState.answered = true;
+ _qState.acertos++;
+ setFeedback(true, 'Marcado como acertado');
+ document.getElementById('comment')?.classList.add('show');
+ document.getElementById('flashcard-btns').innerHTML = '';
+ document.getElementById('btn-next').style.display = '';
+}
+
+function markFlashcardBad() {
+ _qState.answered = true;
+ _qState.erros++;
+ setFeedback(false, 'Marcado como errado');
+ document.getElementById('comment')?.classList.add('show');
+ document.getElementById('flashcard-btns').innerHTML = '';
+ document.getElementById('btn-next').style.display = '';
+}
+
+function setFeedback(ok, msg) {
+ const fb = document.getElementById('feedback');
+ fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad');
+ document.getElementById('feedback-msg').textContent = msg;
+}
+
+function _buildResult() {
+ const { acertos, erros, questions } = _qState;
+ const total = questions.length;
+ const pct = total ? Math.round((acertos / total) * 100) : 0;
+ let nivel, detalhe;
+ if (pct >= 90) {
+ nivel = 'Excelente';
+ detalhe = 'Domínio consistente do conteúdo. Mantenha revisão periódica.';
+ } else if (pct >= 70) {
+ nivel = 'Bom';
+ detalhe = 'Base sólida com pontos a refinar. Revise os erros e releia os comentários.';
+ } else if (pct >= 50) {
+ nivel = 'Regular';
+ detalhe = 'Compreensão parcial, com lacunas importantes. Retome o material teórico.';
+ } else {
+ nivel = 'Insuficiente';
+ detalhe = 'Os conceitos centrais ainda não estão consolidados. Estude o tema e refaça.';
+ }
+ document.getElementById('res-pct').textContent = pct + '%';
+ document.getElementById('res-a').textContent = acertos;
+ document.getElementById('res-a2').textContent = acertos;
+ document.getElementById('res-e').textContent = erros;
+ document.getElementById('res-t').textContent = total;
+ document.getElementById('res-msg-body').innerHTML = `<strong>${nivel}</strong> — ${acertos} de ${total} (${pct}%). ${detalhe}`;
+}
+
+function _saveProgress(key) {
+ try {
+ localStorage.setItem(PROGRESS_PREFIX + key, JSON.stringify({
+ questions: _qState.questions,
+ qIndex: _qState.qIndex,
+ acertos: _qState.acertos,
+ erros: _qState.erros,
+ flipped: _qState.flipped,
+ }));
+ } catch (e) {}
+}
+
+function _loadProgress(key) {
+ try {
+ const s = localStorage.getItem(PROGRESS_PREFIX + key);
+ if (!s) return false;
+ const d = JSON.parse(s);
+ if (!d.questions || !d.questions.length) return false;
+ Object.assign(_qState, {
+ questions: d.questions,
+ qIndex: d.qIndex || 0,
+ acertos: d.acertos || 0,
+ erros: d.erros || 0,
+ flipped: d.flipped || false,
+ });
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+
+function _clearProgress(key) {
+ try {
+ localStorage.removeItem(PROGRESS_PREFIX + key);
+ } catch (e) {}
+}
diff --git a/static/quiz-parser.js b/static/quiz-parser.js
new file mode 100644
index 0000000..c62beb5
--- /dev/null
+++ b/static/quiz-parser.js
@@ -0,0 +1,144 @@
+/**
+ * Quiz Parser - Converte sintaxe customizada em JSON estruturado
+ * Sintaxe suportada:
+ * ? Enunciado da questão
+ * - Opção A / - Opção B
+ * = Opção correta (incluída nas opções automaticamente se ausente)
+ * > Comentário (pode repetir; linha vazia separa parágrafos)
+ *
+ * ? Questão aberta
+ * [ Resposta de referência (pode repetir)
+ * > Comentário
+ *
+ * --- ou linha em branco separam questões
+ * # linha ignorada
+ */
+
+function parseQuizBody(raw) {
+ if (!raw || !raw.trim()) return [];
+ const lines = raw.replace(/\r\n/g, '\n').split('\n');
+ const result = [];
+ let enunciado = '', tipo = '', opcoes = [], gabarito = '', referencia = '', comentario = '';
+
+ function flush() {
+ if (!enunciado.trim()) return;
+ const q = { type: tipo || 'mc', text: enunciado.trim() };
+ if (tipo === 'mc') {
+ q.options = opcoes;
+ q.answer = gabarito;
+ } else {
+ q.reference = referencia.trim();
+ }
+ if (comentario.trim()) q.comment = comentario.trim();
+ result.push(q);
+ enunciado = '';
+ tipo = '';
+ opcoes = [];
+ gabarito = '';
+ referencia = '';
+ comentario = '';
+ }
+
+ for (const rawLine of lines) {
+ const line = rawLine.replace(/\r$/, '').trimEnd();
+ if (/^---+\s*$/.test(line)) {
+ flush();
+ continue;
+ }
+ if (line.trim() === '') continue;
+ const mark = line[0];
+ const content = line.length > 1 ? line.slice(2) : '';
+ switch (mark) {
+ case '#':
+ break;
+ case '?':
+ flush();
+ enunciado = content;
+ break;
+ case '-':
+ opcoes.push(content);
+ tipo = 'mc';
+ break;
+ case '=':
+ gabarito = content;
+ tipo = 'mc';
+ if (content && !opcoes.includes(content)) opcoes.push(content);
+ break;
+ case '[':
+ referencia += (referencia ? '\n' : '') + content;
+ tipo = 'open';
+ break;
+ case '>':
+ comentario += (comentario ? '\n' : '') + content;
+ break;
+ default:
+ if (enunciado && !tipo) enunciado += '\n' + line;
+ else if (tipo === 'open' && referencia) referencia += '\n' + line;
+ }
+ }
+ flush();
+ return result;
+}
+
+/* ── UTILITÁRIOS BASE ── */
+const LETRAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+function shuffle(arr) {
+ const a = [...arr];
+ for (let i = a.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [a[i], a[j]] = [a[j], a[i]];
+ }
+ return a;
+}
+
+function prepareQuizQuestions(questions) {
+ const letterCounts = [];
+ let previousCorrectIndex = -1;
+ return questions.map((q) => {
+ if (!q || q.type !== 'mc' || !Array.isArray(q.options) || q.options.length < 2) {
+ return q;
+ }
+ const answerIndex = q.options.findIndex((o) => o === q.answer);
+ if (answerIndex < 0) {
+ return { ...q, options: shuffle(q.options) };
+ }
+ const optionCount = q.options.length;
+ const availableIndexes = Array.from({ length: optionCount }, (_, i) => i);
+ const lowestCount = Math.min(...availableIndexes.map((i) => letterCounts[i] || 0));
+ let candidates = availableIndexes.filter((i) => (letterCounts[i] || 0) === lowestCount);
+ if (optionCount > 1 && candidates.length > 1) {
+ candidates = candidates.filter((i) => i !== previousCorrectIndex);
+ }
+ const correctIndex = shuffle(candidates)[0] ?? 0;
+ const otherOptions = shuffle(q.options.filter((_, i) => i !== answerIndex));
+ const arrangedOptions = [...otherOptions];
+ arrangedOptions.splice(correctIndex, 0, q.options[answerIndex]);
+ letterCounts[correctIndex] = (letterCounts[correctIndex] || 0) + 1;
+ previousCorrectIndex = correctIndex;
+ return { ...q, options: arrangedOptions };
+ });
+}
+
+/* Para uso em atributos HTML (onclick="...") */
+function esc(s) {
+ return String(s ?? '')
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#39;');
+}
+
+/* Para innerHTML: escapa HTML e renderiza Markdown inline.
+ Suporta: **negrito**, *itálico*, `código`, \n→<br> */
+function md(s) {
+ return String(s ?? '')
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
+ .replace(/`(.+?)`/g, '<code>$1</code>')
+ .replace(/\n/g, '<br>');
+}
diff --git a/static/quiz-runtime.js b/static/quiz-runtime.js
new file mode 100644
index 0000000..d6e824d
--- /dev/null
+++ b/static/quiz-runtime.js
@@ -0,0 +1,92 @@
+/**
+ * Quiz Runtime — controla o fluxo de quiz/simulado na página dedicada
+ */
+
+const PROGRESS_PREFIX = window.PROGRESS_PREFIX = 'quiz_progress_';
+
+// Estado global do quiz (página de quiz dedicada)
+let _qState = {
+ questions: [],
+ qIndex: 0,
+ acertos: 0,
+ erros: 0,
+ answered: false,
+ flipped: false,
+};
+
+function nextQuestion() {
+ _qState.qIndex++;
+ _qState.answered = false;
+ _qState.flipped = false;
+ if (_qState.qIndex >= _qState.questions.length) {
+ showResult();
+ return;
+ }
+ const body = document.getElementById('quiz-body');
+ if (!body) return;
+ body.classList.add('fade-out');
+ setTimeout(() => {
+ body.classList.remove('fade-out');
+ _renderQuestion(_qState.questions[_qState.qIndex]);
+ _saveProgress(window.QUIZ_KEY);
+ }, 100);
+}
+
+function showResult() {
+ const wrap = document.getElementById('quiz-wrap');
+ const result = document.getElementById('quiz-result');
+ if (wrap) wrap.style.display = 'none';
+ if (result) result.style.display = '';
+ _buildResult();
+ _clearProgress(window.QUIZ_KEY);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+}
+
+function restartQuiz() {
+ _clearProgress(window.QUIZ_KEY);
+ _qState.questions = prepareQuizQuestions(parseQuizBody(
+ typeof RAW_QUIZ !== 'undefined' ? RAW_QUIZ : ''
+ ));
+ _qState.qIndex = 0;
+ _qState.acertos = 0;
+ _qState.erros = 0;
+ _qState.answered = false;
+ _qState.flipped = false;
+ const wrap = document.getElementById('quiz-wrap');
+ const result = document.getElementById('quiz-result');
+ if (result) result.style.display = 'none';
+ if (wrap) wrap.style.display = '';
+ _renderQuestion(_qState.questions[0]);
+}
+
+function initQuiz() {
+ // Só inicializa na página de quiz (onde existe #quiz-body)
+ if (!document.getElementById('quiz-body')) return;
+
+ const raw = typeof RAW_QUIZ !== 'undefined' ? RAW_QUIZ : '';
+ const key = window.QUIZ_KEY;
+
+ if (!_loadProgress(key)) {
+ _qState.questions = prepareQuizQuestions(parseQuizBody(raw));
+ _qState.qIndex = 0;
+ _qState.acertos = 0;
+ _qState.erros = 0;
+ }
+
+ _qState.answered = false;
+ _qState.flipped = false;
+
+ const wrap = document.getElementById('quiz-wrap');
+ const result = document.getElementById('quiz-result');
+ if (result) result.style.display = 'none';
+ if (wrap) wrap.style.display = '';
+
+ if (_qState.qIndex >= _qState.questions.length) {
+ showResult();
+ return;
+ }
+
+ _renderQuestion(_qState.questions[_qState.qIndex]);
+}
+
+document.addEventListener('DOMContentLoaded', initQuiz);
diff --git a/static/sw.js b/static/sw.js
new file mode 100644
index 0000000..71f0e5f
--- /dev/null
+++ b/static/sw.js
@@ -0,0 +1,68 @@
+/* Service Worker — caderno de estudos
+ Estratégia: cache-first para assets estáticos, network-first para HTML
+ Versão: 3 (com suporte a stale-while-revalidate) */
+const CACHE = 'caderno-v3';
+const STATIC = [
+ '/',
+ '/offline.html'
+];
+
+self.addEventListener('install', e => {
+ e.waitUntil(
+ caches.open(CACHE).then(c => c.addAll(STATIC)).then(() => self.skipWaiting())
+ );
+});
+
+self.addEventListener('activate', e => {
+ e.waitUntil(
+ caches.keys().then(keys =>
+ Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
+ ).then(() => self.clients.claim())
+ );
+});
+
+self.addEventListener('fetch', e => {
+ const url = new URL(e.request.url);
+
+ // Ignora requisições externas (fontes Google, etc.)
+ if (url.origin !== location.origin) return;
+
+ if (e.request.mode === 'navigate') {
+ // HTML: network-first, fallback para offline.html
+ e.respondWith(
+ fetch(e.request)
+ .then(res => {
+ const clone = res.clone();
+ caches.open(CACHE).then(c => c.put(e.request, clone));
+ return res;
+ })
+ .catch(() => caches.match(e.request).then(r => r || caches.match('/offline.html')))
+ );
+ } else {
+ // Assets (CSS, JS, imagens): stale-while-revalidate
+ e.respondWith(
+ caches.match(e.request).then(cached => {
+ // Se estiver em cache, retorna imediatamente e atualiza em background
+ if (cached) {
+ // Atualizar em background
+ fetch(e.request).then(res => {
+ if (res && res.status === 200) {
+ const clone = res.clone();
+ caches.open(CACHE).then(c => c.put(e.request, clone));
+ }
+ });
+ return cached;
+ }
+
+ // Se não estiver em cache, busca da rede
+ return fetch(e.request).then(res => {
+ if (res && res.status === 200) {
+ const clone = res.clone();
+ caches.open(CACHE).then(c => c.put(e.request, clone));
+ }
+ return res;
+ });
+ })
+ );
+ }
+});
diff --git a/static/toc.js b/static/toc.js
new file mode 100644
index 0000000..950533e
--- /dev/null
+++ b/static/toc.js
@@ -0,0 +1,52 @@
+/**
+ * Table of Contents - Gera sumário automático de headings
+ */
+
+document.addEventListener('DOMContentLoaded', () => {
+ const prose = document.getElementById('prose');
+ const tocNav = document.getElementById('toc-nav');
+ const tocList = document.getElementById('toc-list');
+
+ if (!prose || !tocNav || !tocList) return;
+
+ const headings = prose.querySelectorAll('h2, h3');
+ if (!headings.length) return;
+
+ const used = new Set();
+
+ headings.forEach((h) => {
+ // Gerar ID único baseado no texto
+ let base = h.textContent
+ .trim()
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/^-+|-+$/g, '');
+
+ let id = base || 'sec';
+ let i = 2;
+ while (used.has(id)) id = base + '-' + i++;
+
+ h.id = id;
+ used.add(id);
+
+ // Criar item no sumário
+ const li = document.createElement('li');
+ li.className = 'toc-' + h.tagName.toLowerCase();
+
+ const a = document.createElement('a');
+ a.href = '#' + id;
+ a.textContent = h.textContent;
+ a.addEventListener('click', (e) => {
+ e.preventDefault();
+ h.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ });
+
+ li.appendChild(a);
+ tocList.appendChild(li);
+ });
+
+ // Mostrar sumário se houver conteúdo
+ tocNav.style.display = '';
+});