Hvernig viltu læra?

Appið les eina línu — þú endurtekur hana. Smelltu svo á næstu.

Rödd

👩
Kona
👨
Karl
HægtEðlilegtHratt

⚡ Byrja strax

Faðirvor er þegar hlaðið inn — smelltu bara hér!

Texti til að læra

Veldu .txt skrá
eða dragðu hér

Eða límdu textann hér:

Lína 1 / 1 0%
Lína 1
Hlusta...

🔁 Endurtaktu línuna — svo farðu áfram

<div id="lrd-app-body">
<style scoped>
#lrd-app-body {
  font-family: 'DM Sans', sans-serif;
  background: #FDFAF5;
  min-height: 100vh;
  padding-bottom: 60px;
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg:#FDFAF5;--bg2:#F4EFE5;--bg3:#EDE6D6;--card:#FFFFFF;
  --text:#1A1612;--text2:#6B6055;--text3:#A09080;
  --border:rgba(90,70,50,0.12);--border2:rgba(90,70,50,0.22);
  --blue:#2B6CB0;--blue-bg:#EBF4FF;
  --green:#276749;--green-bg:#E6F4EC;
  --amber:#92400E;--amber-bg:#FEF3C7;
  --purple:#553C9A;--purple-bg:#EBE5FF;
  --radius:16px;--radius-sm:10px;
}
#lrd-app-body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;padding-bottom:60px}
.topbar{background:var(--card);border-bottom:0.5px solid var(--border);padding:16px 20px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:10}
.topbar-title{font-family:'Fraunces',serif;font-size:22px;font-weight:600;color:var(--text);letter-spacing:-0.02em}
.topbar-sub{font-size:13px;color:var(--text3)}
.container{max-width:560px;margin:0 auto;padding:24px 16px}
.card{background:var(--card);border:0.5px solid var(--border);border-radius:var(--radius);padding:22px;margin-bottom:16px}
.screen{display:none}.screen.active{display:block}
h2{font-family:'Fraunces',serif;font-size:24px;font-weight:500;color:var(--text);margin-bottom:8px;letter-spacing:-0.02em}
h3{font-size:16px;font-weight:500;color:var(--text);margin-bottom:10px}
p{font-size:15px;color:var(--text2);line-height:1.6;margin-bottom:10px}
.hint{font-size:13px;color:var(--text3);margin-top:4px;line-height:1.5}
textarea{width:100%;padding:12px 14px;font-size:14px;border:0.5px solid var(--border2);border-radius:var(--radius-sm);background:var(--bg);color:var(--text);font-family:monospace;line-height:1.7;outline:none;resize:vertical;min-height:160px}
.file-drop{border:1.5px dashed var(--border2);border-radius:var(--radius-sm);padding:26px;text-align:center;cursor:pointer;transition:background 0.15s,border-color 0.15s;margin-bottom:12px}
.file-drop:hover,.file-drop.dragover{background:var(--bg2);border-color:var(--blue)}
.file-drop input{display:none}
.file-loaded{font-size:14px;color:var(--green);font-weight:500;margin-top:8px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:14px 22px;font-size:16px;font-weight:500;border:0.5px solid var(--border2);border-radius:var(--radius-sm);background:var(--card);color:var(--text);cursor:pointer;transition:background 0.15s,transform 0.1s;font-family:'DM Sans',sans-serif;white-space:nowrap}
.btn:hover{background:var(--bg2)}.btn:active{transform:scale(0.97)}
.btn:disabled{opacity:0.35;cursor:not-allowed;transform:none;pointer-events:none}
.btn-primary{background:var(--text);color:var(--bg);border-color:var(--text)}.btn-primary:hover{background:#3D342B}
.btn-green{background:var(--green);color:white;border-color:var(--green)}.btn-green:hover{background:#1D4E38}
.btn-blue{background:var(--blue);color:white;border-color:var(--blue)}.btn-blue:hover{background:#1A4A87}
.btn-purple{background:var(--purple);color:white;border-color:var(--purple)}.btn-purple:hover{background:#3B2780}
.btn-full{width:100%}.btn-xl{padding:18px 28px;font-size:18px;border-radius:var(--radius)}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.divider{height:0.5px;background:var(--border);margin:16px 0}
.voice-pick{display:flex;gap:10px;margin-bottom:16px}
.vbtn{flex:1;padding:14px 10px;border:2px solid var(--border2);border-radius:var(--radius-sm);text-align:center;cursor:pointer;transition:all 0.15s;background:var(--bg)}
.vbtn.sel-f{border-color:var(--green);background:var(--green-bg)}
.vbtn.sel-m{border-color:var(--blue);background:var(--blue-bg)}
.vbtn-icon{font-size:28px;margin-bottom:6px}
.vbtn-label{font-size:15px;font-weight:500}
.vbtn.sel-f .vbtn-label{color:var(--green)}
.vbtn.sel-m .vbtn-label{color:var(--blue)}
.progress-wrap{margin-bottom:18px}
.progress-track{height:6px;background:var(--bg3);border-radius:3px;overflow:hidden}
.progress-fill{height:100%;background:var(--text);border-radius:3px;transition:width 0.4s ease}
.progress-label{display:flex;justify-content:space-between;margin-bottom:8px}
.progress-label span{font-size:13px;color:var(--text3)}
.stage{background:var(--card);border-radius:var(--radius);border:0.5px solid var(--border);padding:32px 24px;text-align:center;margin-bottom:16px;min-height:200px;display:flex;flex-direction:column;align-items:center;justify-content:center}
.stage.memo{border-top:4px solid var(--purple)}
.stage-num{font-size:12px;font-weight:500;letter-spacing:0.08em;text-transform:uppercase;color:var(--purple);margin-bottom:14px}
.stage-text{font-family:'Fraunces',serif;font-size:26px;font-weight:400;line-height:1.55;color:var(--text);transition:filter 0.3s}
.stage-text.blurred{filter:blur(10px);user-select:none}
.wave-row{display:flex;align-items:center;justify-content:center;gap:5px;height:44px;margin:8px 0}
.wave-bar{width:5px;background:var(--purple);border-radius:3px;animation:wavePulse 0.7s ease-in-out infinite}
.wave-bar:nth-child(1){animation-delay:0s}.wave-bar:nth-child(2){animation-delay:0.1s}
.wave-bar:nth-child(3){animation-delay:0.2s}.wave-bar:nth-child(4){animation-delay:0.3s}
.wave-bar:nth-child(5){animation-delay:0.2s}.wave-bar:nth-child(6){animation-delay:0.1s}
@keyframes wavePulse{0%,100%{height:8px;opacity:0.3}50%{height:32px;opacity:1}}
.wave-row.idle .wave-bar{animation:none;height:8px;opacity:0.3}
.status{font-size:14px;color:var(--text3);text-align:center;min-height:22px;margin-bottom:12px}
.status.speaking{color:var(--purple)}.status.kid-turn{color:var(--green);font-weight:500}.status.err{color:#9B2C2C}
.memo-prompt{background:var(--purple-bg);border-radius:var(--radius-sm);padding:14px 18px;text-align:center;margin-bottom:14px;border:0.5px solid rgba(85,60,154,0.2)}
.memo-prompt p{color:var(--purple);font-size:15px;font-weight:500;margin:0}
.controls{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:10px}
.finish-state{text-align:center;padding:24px 0}
.finish-icon{font-size:64px;margin-bottom:14px}
.finish-title{font-family:'Fraunces',serif;font-size:28px;font-weight:500;margin-bottom:10px}
.finish-sub{font-size:16px;color:var(--text2)}
.speed-row{margin-top:14px}
.speed-row label{font-size:14px;color:var(--text2);font-weight:500;display:block;margin-bottom:6px}
@media(max-width:480px){.stage-text{font-size:22px}.container{padding:16px 12px}}

</style>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@400;500;600&family=DM+Sans:wght@400;500&display=swap" rel="stylesheet">
<div class="topbar">
  <div class="topbar-title">📖 Textalærdómur</div>

<div class="container">


<!-- SCREEN 1: SETUP -->
<div class="screen active" id="screen-setup">
  <div style="margin-bottom:20px">
    <h2>Hvernig viltu læra?</h2>
    <p>Appið les eina línu — þú endurtekur hana. Smelltu svo á næstu.</p>
  </div>

  <div class="card">
    <h3>Rödd</h3>
    <div class="voice-pick">
      <div class="vbtn sel-f" id="vbtn-f" onclick="setVoice('f')">
        <div class="vbtn-icon">👩</div>
        <div class="vbtn-label">Kona</div>
      </div>
      <div class="vbtn" id="vbtn-m" onclick="setVoice('m')">
        <div class="vbtn-icon">👨</div>
        <div class="vbtn-label">Karl</div>
      </div>
    </div>
    <div class="speed-row">
      <label>Hraði — <span id="speed-label">eðlilegur (1.0)</span></label>
      <input type="range" id="speed-slider" min="0.6" max="1.5" step="0.1" value="1.0"
        oninput="updateSpeed(this.value)"
        style="width:100%;accent-color:var(--text)">
      <div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text3);margin-top:4px">
        <span>Hægt</span><span>Eðlilegt</span><span>Hratt</span>
      </div>
    </div>
  </div>

  <div class="card" style="border:2px solid var(--green);background:var(--green-bg)">
    <h3 style="color:var(--green)">⚡ Byrja strax</h3>
    <p style="color:var(--green);margin-bottom:14px">Faðirvor er þegar hlaðið inn — smelltu bara hér!</p>
    <button class="btn btn-green btn-full btn-xl" onclick="startFadirvor()">🙏 Æfa Faðirvor</button>
  </div>
  <div class="card">
    <h3>Texti til að læra</h3>
    <div class="file-drop" id="file-drop" onclick="document.getElementById('file-input').click()">
      <input type="file" id="file-input" accept=".txt,.text"/>
      <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="color:var(--text3);margin-bottom:10px"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
      <div style="font-size:15px;color:var(--text2)">Veldu .txt skrá</div>
      <div style="font-size:13px;color:var(--text3);margin-top:4px">eða dragðu hér</div>
    </div>
    <div id="file-loaded" style="display:none" class="file-loaded"></div>
    <div class="divider"></div>
    <p style="margin:0 0 8px;font-size:14px;color:var(--text2)">Eða límdu textann hér:</p>
    <textarea id="paste-input" placeholder="Faðir vor, sem ert á himnum,
helgist þitt nafn,
til komi þitt ríki,
verði þinn vilji..."></textarea>
  </div>

  <button class="btn btn-primary btn-full btn-xl" onclick="startMemo()">Byrja æfingu →</button>
</div>

<!-- SCREEN 2: PRACTICE -->
<div class="screen" id="screen-practice">
  <div class="progress-wrap">
    <div class="progress-label">
      <span id="prog-label">Lína 1 / 1</span>
      <span id="prog-pct">0%</span>
    </div>
    <div class="progress-track"><div class="progress-fill" id="prog-fill" style="width:0%"></div></div>
  </div>

  <div class="stage memo" id="stage">
    <div class="stage-num" id="stage-num">Lína 1</div>
    <div class="stage-text blurred" id="stage-text">—</div>
  </div>

  <div id="wave-wrap" style="display:none">
    <div class="wave-row idle" id="wave-row">
      <div class="wave-bar"></div><div class="wave-bar"></div>
      <div class="wave-bar"></div><div class="wave-bar"></div>
      <div class="wave-bar"></div><div class="wave-bar"></div>
    </div>
  </div>

  <div class="status" id="status">Hlusta...</div>

  <div class="memo-prompt">
    <p>🔁 Endurtaktu línuna — svo farðu áfram</p>
  </div>

  <div class="controls">
    <button class="btn btn-purple" onclick="replayLine()">
      <svg width="15" height="15" viewBox="0 0 14 14" fill="currentColor"><path d="M2 1.5l11 5.5-11 5.5z"/></svg>
      Endurtaka
    </button>
    <button class="btn btn-green" onclick="revealLine()">Sýna línu</button>
    <button class="btn btn-primary btn-xl" id="btn-next" onclick="nextLine()">Næsta →</button>
  </div>

  <div style="margin-top:28px;text-align:center">
    <button class="btn" style="font-size:13px;padding:8px 16px;color:var(--text3)" onclick="showScreen('screen-setup','Veldu texta')">← Til baka</button>
  </div>
</div>


</div>
<script>

const API_KEY = 'AIzaSyD4it8o1iU1fqleyI-7LciN28UwKl0yxN4';

let lines = [];
let currentIdx = 0;
let speaking = false;
let voiceGender = 'f';
let speechRate = 1.0;
let currentLineText = '';

// ── Voice/speed ────────────────────────────────────────
function setVoice(g) {
  voiceGender = g;
  const f = document.getElementById('vbtn-f');
  const m = document.getElementById('vbtn-m');
  f.className = 'vbtn' + (g==='f' ? ' sel-f' : '');
  m.className = 'vbtn' + (g==='m' ? ' sel-m' : '');
}

function updateSpeed(v) {
  speechRate = parseFloat(v);
  const labels = {'0.6':'mjög hægt','0.7':'hægt','0.8':'nokkuð hægt','0.9':'aðeins hægt','1.0':'eðlilegt','1.1':'aðeins hratt','1.2':'frekar hratt','1.3':'hratt','1.4':'mjög hratt','1.5':'hámark'};
  const el = document.getElementById('speed-label');
  if (el) el.textContent = (labels[parseFloat(v).toFixed(1)] || v) + ' (' + parseFloat(v).toFixed(1) + ')';
}

// ── File loading ───────────────────────────────────────
const fileDrop = document.getElementById('file-drop');
const fileInput = document.getElementById('file-input');
fileDrop.addEventListener('dragover', e=>{e.preventDefault();fileDrop.classList.add('dragover');});
fileDrop.addEventListener('dragleave', ()=>fileDrop.classList.remove('dragover'));
fileDrop.addEventListener('drop', e=>{e.preventDefault();fileDrop.classList.remove('dragover');const f=e.dataTransfer.files[0];if(f)loadFile(f);});
fileInput.addEventListener('change', e=>{if(e.target.files[0])loadFile(e.target.files[0]);});

function hasGarbled(t){return t.includes('\uFFFD')||/[ÃðþæöáéíóúýÐÞÆÖÁÉÍÓÚÝ]/.test(t);}
function loadFile(f){
  const tryRead=(enc,fb)=>{
    const r=new FileReader();
    r.onload=ev=>{
      const t=ev.target.result;
      if(fb&&hasGarbled(t)){tryRead(fb,null);return;}
      document.getElementById('paste-input').value=t;
      document.getElementById('file-loaded').style.display='block';
      document.getElementById('file-loaded').textContent='✓ '+f.name+' hlaðið upp';
    };
    r.readAsText(f,enc);
  };
  tryRead('UTF-8','windows-1252');
}

// ── Start ──────────────────────────────────────────────
// Faðirvor baked in
const FADIRVOR_LINES = ["Faðir vor, sem ert á himnum,","helgist þitt nafn,","til komi þitt ríki,","verði þinn vilji,","svo á jörðu sem á himni.","Gef oss í dag vort daglegt brauð,","og fyrirgef oss vorar skuldir,","svo sem vér og fyrirgefum vorum skuldunautum,","og eigi leið oss í freistni,","heldur frelsa oss frá illu.","Því að þitt er ríkið, mátturinn og dýrðin","að eilífu.","Amen."];

function startMemo() {
  const raw = document.getElementById('paste-input').value.trim();
  lines = raw ? raw.split('\n').map(l=>l.trim()).filter(Boolean) : FADIRVOR_LINES;
  if (lines.length === 0) { alert('Enginn texti fannst.'); return; }
  currentIdx = 0;
  speaking = false;
  showScreen('screen-practice', 'Æfing');
  renderLine();
}

function startFadirvor() {
  lines = FADIRVOR_LINES;
  currentIdx = 0;
  speaking = false;
  showScreen('screen-practice', 'Æfing');
  renderLine();
}

// ── Render ─────────────────────────────────────────────
function renderLine() {
  if (currentIdx >= lines.length) { showFinish(); return; }
  currentLineText = lines[currentIdx];
  const total = lines.length;
  const pct = Math.round((currentIdx / total) * 100);

  document.getElementById('prog-fill').style.width = pct + '%';
  document.getElementById('prog-label').textContent = 'Lína ' + (currentIdx+1) + ' / ' + total;
  document.getElementById('prog-pct').textContent = pct + '%';
  document.getElementById('stage-num').textContent = 'Lína ' + (currentIdx+1);
  document.getElementById('stage-text').textContent = currentLineText;
  document.getElementById('stage-text').classList.add('blurred');
  document.getElementById('wave-wrap').style.display = 'none';
  document.getElementById('btn-next').disabled = false;

  setStatus('Hlusta...', 'speaking');
  setTimeout(() => playLine(), 400);
}

function revealLine() {
  document.getElementById('stage-text').classList.remove('blurred');
}

function replayLine() {
  playLine();
}

function nextLine() {
  currentIdx++;
  renderLine();
}

// ── TTS ────────────────────────────────────────────────
async function playLine() {
  if (speaking) return;
  speaking = true;
  setWave(true);
  setStatus('Les upphátt...', 'speaking');

  const waveNet = voiceGender==='f' ? 'is-IS-Wavenet-A' : 'is-IS-Wavenet-B';
  const standard = voiceGender==='f' ? 'is-IS-Standard-A' : 'is-IS-Standard-B';

  const tryVoice = async (vName) => {
    const res = await fetch('https://texttospeech.googleapis.com/v1/text:synthesize?key=' + encodeURIComponent(API_KEY), {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({
        input: {text: currentLineText},
        voice: {languageCode:'is-IS', name:vName},
        audioConfig: {audioEncoding:'MP3', speakingRate:speechRate, pitch:voiceGender==='m'?-2:0}
      })
    });
    return res.json();
  };

  try {
    let data = await tryVoice(waveNet);
    if (!data.audioContent) data = await tryVoice(standard);
    if (!data.audioContent && standard === 'is-IS-Standard-B') data = await tryVoice('is-IS-Standard-A');
    if (data.audioContent) {
      await playMp3(data.audioContent);
    } else {
      const msg = data.error && data.error.message ? data.error.message : 'Óþekkt villa';
      setStatus('Villa: ' + msg, 'err');
    }
  } catch(e) {
    console.error(e);
    setStatus('Tenging mistókst', 'err');
  }

  speaking = false;
  setWave(false);
  setStatus('Endurtaktu línuna', 'kid-turn');
}

function playMp3(b64) {
  return new Promise(resolve => {
    const a = new Audio('data:audio/mp3;base64,' + b64);
    a.onended = resolve; a.onerror = resolve;
    a.play().catch(resolve);
  });
}

// ── Finish ─────────────────────────────────────────────
function showFinish() {
  const stage = document.getElementById('stage');
  stage.className = 'stage';
  stage.innerHTML = '<div class="finish-state"><div class="finish-icon">🌟</div><div class="finish-title">Frábært!</div><div class="finish-sub">Þú kláraðir allan textann!</div></div>';
  document.getElementById('prog-fill').style.width = '100%';
  document.getElementById('prog-pct').textContent = '100%';
  document.getElementById('wave-wrap').style.display = 'none';
  document.querySelector('.memo-prompt').style.display = 'none';
  document.querySelector('.controls').style.display = 'none';
  setStatus('');
}

function showScreen(id, label) {
  document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));
  document.getElementById(id).classList.add('active');
  document.getElementById('topbar-sub').textContent = label || '';
  window.scrollTo(0, 0);
}

function setStatus(msg, type) {
  const el = document.getElementById('status');
  el.textContent = msg;
  el.className = 'status' + (type ? ' '+type : '');
}
function setWave(on) {
  document.getElementById('wave-wrap').style.display = on ? 'block' : 'none';
  document.getElementById('wave-row').className = 'wave-row' + (on ? '' : ' idle');
}

</script>
</div>