/* Yogavibe — Journal: article data + list page + article page */

const JOURNAL_POSTS = [
  {
    id: 'riad-breakfast-msemen',
    n: '01',
    cat: 'Recipe',
    date: 'Apr 2026',
    isoDate: '2026-04-10',
    title: 'The riad breakfast msemen',
    excerpt: 'Fatima has been making msemen at the riad every morning since before we arrived. After three years of asking, she finally showed me how. The secret is in the fold — and in using your hands, not a rolling pin.',
    read: '4 min',
    img: 'images/real-long-lunch.jpg',
    pos: 'center',
    metaDesc: 'How to make msemen — the flaky Moroccan flatbread served every morning at the Yogavibe riad in Essaouira. Recipe with method, tips and ingredient notes.',
    body: [
      { type: 'p', text: 'Msemen are flaky, layered Moroccan flatbreads somewhere between a crêpe and a laminated pastry. They are served warm at the riad every morning alongside argan oil, honey, and amlou — a thick paste of roasted almonds, argan oil and honey that is one of the best things I have eaten anywhere. The recipe looks simple. The technique is not.' },
      { type: 'p', text: 'Fatima has been making them since before I started renting the riad. She is faster than I will ever be — pressing, folding, slapping the pan, turning — a practised rhythm built over decades. What follows is my best attempt to transcribe what I watched.' },
      { type: 'h2', text: 'Ingredients (makes 8)' },
      { type: 'ul', items: [
        '250 g fine semolina (fark — the finest grade you can find)',
        '250 g plain flour',
        '1 tsp salt',
        '1 tsp sugar',
        '7 g instant yeast',
        '300 ml warm water, approximately',
        'Vegetable oil and softened butter, for folding',
      ]},
      { type: 'h2', text: 'Method' },
      { type: 'p', text: 'Mix the semolina, flour, salt, sugar and yeast in a large bowl. Add warm water gradually and knead for 10 minutes. The dough should be soft and smooth without sticking to your hands. If it tears, it needs more water. If it sticks, add flour a tablespoon at a time.' },
      { type: 'p', text: 'Divide into 8 equal balls. Cover with a damp cloth and rest for 15 minutes. This is not optional.' },
      { type: 'p', text: 'Oil your work surface generously. Take one ball and press it flat into a thin circle using only your palm — spread from the centre outward in firm circular motions. The dough should become almost translucent. Dot a teaspoon of softened butter across the surface and fold the edges inward like an envelope: left in, right in, top down, bottom up. You now have a rough square. Fold it in half, then in half again. Rest for another 10 minutes.' },
      { type: 'p', text: 'Press each rested square gently flat — use your palms, not a rolling pin. A rolling pin compresses the layers. Your hands preserve them.' },
      { type: 'p', text: 'Cook in a dry, heavy skillet over medium heat, 2 to 3 minutes per side, until golden with darker blisters. Serve immediately.' },
      { type: 'h2', text: 'A note on ingredients' },
      { type: 'p', text: 'Moroccan semolina (fark) is finer than anything sold in European supermarkets. If you can find it at a North African grocer, use it. Otherwise, the finest durum wheat semolina you have will work — the texture will be slightly chewier, the flavour the same.' },
      { type: 'p', text: 'Msemen do not keep. They are meant to be eaten hot off the pan, torn and dipped, standing in the kitchen if necessary.' },
      { type: 'note', text: 'At the riad we serve these with Siwa Valley argan oil — cold-pressed, golden, with a nuttiness that bears no resemblance to the cosmetic kind. If you can find culinary argan oil, this is the moment to use it.' },
    ],
  },
  {
    id: 'reading-list-solo-travel',
    n: '02',
    cat: 'Reading',
    date: 'Mar 2026',
    isoDate: '2026-03-18',
    title: 'A reading list for solo travel',
    excerpt: 'Sixty percent of Yogavibe guests arrive alone. Below is the list I have been building since 2017, updated each year with whatever has been left on the terrace or recommended over dinner.',
    read: '6 min',
    img: 'uploads/books.png',
    pos: 'center',
    metaDesc: 'A curated reading list for solo travellers heading to Morocco or a yoga retreat. Books on travel, the body, breath, yoga, and being alone well — recommended by Charlotte Schoeters of Yogavibe.',
    body: [
      { type: 'p', text: 'Sixty percent of Yogavibe guests come alone. They land in Essaouira, take the airport transfer, and by the second morning they are sharing a table with nine strangers who feel less like strangers every hour. But the flight, the transit, the first night in a new room — these are where the books matter.' },
      { type: 'p', text: 'Below is the list I have been building since 2017. It covers the practical and the philosophical, the things that make solo travel feel like a choice rather than a circumstance.' },
      { type: 'h2', text: 'On being alone well' },
      { type: 'h3', text: 'A Field Guide to Getting Lost — Rebecca Solnit' },
      { type: 'p', text: 'The book most often left behind at the riad. Solnit writes about deliberate disorientation — about stepping outside the known and allowing the unknown to teach you something. For anyone nervous about travelling solo, it reframes aloneness as a practice rather than a problem. It is not a travel book. It is better than a travel book.' },
      { type: 'h3', text: 'The Sheltering Sky — Paul Bowles' },
      { type: 'p', text: 'Set in North Africa. Not a comfortable read, but an essential one if you are going to Morocco with your eyes fully open. Bowles lived in Tangier for decades; his characters move through the Maghreb as strangers who never quite arrive — which is, in part, the point. Read it before you go, not during.' },
      { type: 'h3', text: 'Siddhartha — Hermann Hesse' },
      { type: 'p', text: 'A cliché for good reason. Best read slowly, one chapter per morning with coffee. The retreat format — structured time, rest, practice, reflection — creates exactly the conditions this book needs.' },
      { type: 'h2', text: 'On the body and practice' },
      { type: 'h3', text: 'The Body Keeps the Score — Bessel van der Kolk' },
      { type: 'p', text: 'The science of how stress accumulates in the body and how movement, breath, and presence help release it. I recommend this to every guest who arrives visibly wound tight. The retreat makes considerably more sense once you understand the mechanism.' },
      { type: 'h3', text: 'Breath — James Nestor' },
      { type: 'p', text: 'Short, evidence-based, and quietly transformative. Nestor\'s research on nasal breathing changed my own morning practice. I recommend it to anyone who arrives mouth-breathing through their first vinyasa — which is often more people than you would expect.' },
      { type: 'h3', text: 'Light on Yoga — B.K.S. Iyengar' },
      { type: 'p', text: 'The reference text. Bring it only if you intend to study; it is heavy and not casual reading. But for anyone serious about their practice, there is nothing quite like it.' },
      { type: 'h2', text: 'On Morocco' },
      { type: 'h3', text: 'In Morocco — Edith Wharton (1920)' },
      { type: 'p', text: 'Colonial in perspective but extraordinarily observant. Wharton\'s descriptions of Fes, Marrakech and Essaouira remain accurate in ways that feel uncanny more than a century later. The light she describes is still the light.' },
      { type: 'h2', text: 'A note on reading at retreats' },
      { type: 'p', text: 'On the first evening most guests are still checking their phones. By day three the books come out properly. By day five some people stop reading altogether. That is the point. The reading list is not the destination. It is the bridge.' },
    ],
  },
  {
    id: 'what-to-pack-yoga-retreat-morocco',
    n: '03',
    cat: 'Guide',
    date: 'Mar 2026',
    isoDate: '2026-03-01',
    title: 'What to pack for a yoga retreat in Morocco',
    excerpt: 'Most guests overpack the clothes and underpack the patience. A practical, honest packing guide for a week at the riad in Essaouira: what you actually need, and what you can leave behind.',
    read: '5 min',
    img: 'uploads/what to pack.png',
    pos: 'center',
    metaDesc: 'Practical packing guide for a yoga retreat in Essaouira, Morocco — what to wear, what yoga gear to bring, hammam essentials, and what to leave at home. From the Yogavibe host.',
    body: [
      { type: 'p', text: 'Every retreat I watch guests arrive with bags that are too heavy and anxieties that are too specific. By day three the bags are unpacked and the anxieties have dissolved. This list is an attempt to save you the first part.' },
      { type: 'h2', text: 'Clothes' },
      { type: 'p', text: 'Essaouira in retreat season runs warm by day, comfortable in a t-shirt from mid-morning, and noticeably cooler in the evenings once the sun drops. The wind is present all week. It is not cold wind, but it is consistent and strong enough that you want a layer for the roof terrace in the mornings and for walking through the medina at night.' },
      { type: 'ul', items: [
        '3–4 yoga outfits (we practise twice daily; you will sweat)',
        '1 light layer for morning practice and cool evenings: a thin hoodie or linen shirt works well',
        '1 warmer layer for the occasional cold night: a fleece or knit jumper',
        'Comfortable clothes for the afternoons: light trousers, a dress, whatever you move easily in',
        '1 slightly nicer outfit if you want it for dinner out in the medina',
        'Sandals for around the riad and medina; trainers or closed shoes for longer walks',
        'A light scarf, useful for wind, medina visits, and doubling as a beach wrap',
      ]},
      { type: 'p', text: 'You do not need much. The riad has laundry facilities for longer stays, and the medina has shops if you forget something essential.' },
      { type: 'h2', text: 'Yoga gear' },
      { type: 'p', text: 'The riad provides mats, blocks and straps. You do not need to bring your own mat unless you have strong preferences about the feel of it. Some guests do, and that is fine. What to bring:' },
      { type: 'ul', items: [
        'A yoga towel if you run hot; the roof terrace classes can get warm once the sun is up',
        'Any props you genuinely rely on: a specific block height, a meditation cushion, a strap you trust',
        'Grip socks if you use them (optional; most guests practise barefoot)',
      ]},
      { type: 'h2', text: 'For the hammam' },
      { type: 'p', text: 'One of the weekly activities is an afternoon at a traditional hammam in the medina. You will be scrubbed down with a kessa mitt and black soap. Everything you need is provided there. What helps to bring:' },
      { type: 'ul', items: [
        'A swimsuit or shorts; some guests prefer these to the towels provided, both are fine',
        'Flip flops for the changing area',
        'Old underwear you don\'t mind getting damp',
        'A spare hair tie if your hair is long',
      ]},
      { type: 'p', text: 'Leave your expensive moisturiser at the hotel. The hammam attendants will apply argan oil after the scrub. Your skin will not need anything else that evening.' },
      { type: 'h2', text: 'Practical things' },
      { type: 'ul', items: [
        'Sun protection: SPF for face and body, a hat for afternoon activities in direct sun',
        'A reusable water bottle (the riad keeps filtered water available all day)',
        'Any prescription medication you take, plus a small first-aid kit if you travel with one',
        'Earplugs if you are a light sleeper; the medina has a 4 a.m. call to prayer that is beautiful the first morning and occasionally less so at the end of the week',
        'A journal or notebook; most guests end up using one even if they didn\'t plan to',
      ]},
      { type: 'h2', text: 'What to leave behind' },
      { type: 'p', text: 'Your laptop, if you can. A retreat week is seven days; most things can wait. If you cannot fully disconnect, set an out-of-office, block two hours in the evenings, and leave the rest. The riad has wifi. The temptation to use it beyond the necessary is the main obstacle.' },
      { type: 'p', text: 'Formal clothes. You will not need them. The riad dinner is relaxed, the medina restaurants are casual, and anything that requires ironing will spend the week at the bottom of your bag.' },
      { type: 'note', text: 'Questions about what to expect or what to bring? Email charlotte@yogavibe.eu. I answer these personally and always will.' },
    ],
  },
  {
    id: 'notes-from-the-winter-reset',
    n: '04',
    cat: 'Dispatch',
    date: 'Mar 2026',
    isoDate: '2026-03-15',
    title: 'Notes from the winter reset',
    excerpt: 'March in Essaouira: warm enough by day to practise on the roof in a t-shirt, cool enough at night to sleep under a blanket, and windy enough all week to clear out whatever you brought in. Below are the notes I wrote on the last morning.',
    read: '5 min',
    img: 'uploads/off season.png',
    pos: 'center',
    metaDesc: 'Charlotte Schoeters on returning to Essaouira, Morocco in March for the annual winter reset — warm spring days, strong Atlantic wind, cool nights, and a week of unstructured practice.',
    body: [
      { type: 'p', text: 'March is the reason we chose March. By midday the sun is warm enough to sit on the roof in a t-shirt and mean it. The tourist season hasn\'t started yet, so the medina is quiet, the streets belonging to the people who live there, which is the version of Essaouira worth knowing. And the wind is still very much doing its thing.' },
      { type: 'p', text: 'I have been coming back at this time of year for three years running. Not for a retreat, just to be in the place. Below are the notes I wrote on the last morning before flying home to Antwerp.' },
      { type: 'h2', text: 'Arrival' },
      { type: 'p', text: 'I flew into Marrakech and took a taxi straight to Essaouira, about two and a half hours through the plain, the light changing as you get closer to the coast. I arrived late. There was a pot of harira on the stove. I ate alone in the kitchen at midnight, the riad completely silent around me. Sleep came fast and without effort, which is what this place does.' },
      { type: 'h2', text: 'On the wind' },
      { type: 'p', text: 'The alizé blows north off the Atlantic pretty much year-round, but in March it has a particular quality: strong and consistent, carrying just enough salt and cold air to keep things honest. By day the sun more than compensates. You practise on the roof in the warmth, the wind pulling at your clothes, the Atlantic visible between the rooftops. By evening, when the sun drops, you add a layer and you\'re glad you brought one.' },
      { type: 'p', text: 'Nights are cool, genuinely cool, not just slightly less hot. You sleep under a proper blanket. This turns out to be exactly the right condition for sleeping well, which is not always something you can arrange at home.' },
      { type: 'h2', text: 'The practice' },
      { type: 'p', text: 'We practised on the roof with a windbreak rigged from old Berber blankets. The carpet collected sand by the end of the week. Three of us: two teachers, a small group. No fixed timetable except morning practice and meals. We improvised the rest.' },
      { type: 'p', text: 'Off-season practice has a different quality to retreat yoga. There are no guests to hold space for, no sequence planned in advance. The practice follows the day rather than leading it. We started later when the sun reached the terrace. We stayed in forward folds longer than a class schedule would allow. One morning we practised for two hours without planning to.' },
      { type: 'h2', text: 'What changed' },
      { type: 'p', text: 'Less than I expected. More than I can name. The body remembers the mat differently after a week of daily unstructured practice: it arrives faster, settles deeper. The mind does something similar. The noise of ordinary life takes longer to return, and when it does it sounds slightly different, less urgent, more optional.' },
      { type: 'h2', text: 'On returning' },
      { type: 'p', text: 'I landed in Brussels to find my inbox full, a broken pipe in the kitchen, and four voicemails from my mother. The pipe was fixed within an hour. The inbox took four days. My mother and I talked on the phone for a long time, which we had not done properly in months.' },
      { type: 'p', text: 'This is also what a week away is for.' },
      { type: 'note', text: 'March is one of the best months to visit Essaouira: warm days, quiet streets, lower flights. If you\'re considering a custom or private retreat outside peak season, get in touch: charlotte@yogavibe.eu.' },
    ],
  },
  {
    id: 'khadija-preserved-lemon-tagine',
    n: '05',
    cat: 'Recipe',
    date: 'Feb 2026',
    isoDate: '2026-02-05',
    title: "Our chef's preserved lemon tagine",
    excerpt: 'Our chef has cooked at the riad since 2019. She has never written this recipe down. What follows is my best attempt at transcription, built from two hours of watching and several months of failed attempts in my Antwerp kitchen.',
    read: '7 min',
    img: 'uploads/tajine.png',
    pos: 'center',
    metaDesc: 'Authentic Moroccan chicken tagine with preserved lemon and green olives — the recipe served at the Yogavibe riad in Essaouira, transcribed from the riad chef.',
    body: [
      { type: 'p', text: 'Our chef comes from the medina. She has three children and has been cooking at the riad since 2019. She makes this tagine from memory, adjusting by smell and touch, never measuring anything. She is faster and more accurate than any recipe can fully describe.' },
      { type: 'p', text: 'The version below is my transcription, built from two hours watching her work and then several months of failed attempts in my Antwerp apartment. The gap between what I saw and what I could replicate is the gap where a lifetime of cooking lives.' },
      { type: 'h2', text: 'Ingredients (serves 4–6)' },
      { type: 'ul', items: [
        '1 whole chicken, jointed, or 6–8 bone-in thighs',
        '2 preserved lemons, skin only, rinsed of excess salt',
        '200 g green olives, pitted',
        '2 large onions, very finely grated',
        '4 garlic cloves, crushed to a smooth paste',
        '1 tsp ground ginger',
        '1 tsp ground cumin',
        '½ tsp turmeric',
        '½ tsp black pepper',
        '1 generous pinch of saffron threads, steeped in 3 tbsp of hot water for at least 20 minutes',
        '3 tbsp good olive oil',
        'A small bunch of fresh coriander and flat-leaf parsley, tied together',
        'Salt to taste (the preserved lemons are already very salty, so add carefully)',
      ]},
      { type: 'h2', text: 'Method' },
      { type: 'p', text: 'Mix the grated onion, garlic, ginger, cumin, turmeric, black pepper and olive oil in the base of a heavy pot or tagine. Add the chicken pieces and turn to coat thoroughly. Cover and leave to marinate for at least one hour. Overnight produces a noticeably different result.' },
      { type: 'p', text: 'Pour the saffron water over the chicken. Add enough plain water to come halfway up the sides of the meat, approximately 250 ml depending on the width of your pot. Place the herb bundle in the centre. Bring to a simmer over medium heat, then reduce to very low. Cover and cook for one hour, turning the chicken once halfway through.' },
      { type: 'p', text: 'Add the preserved lemon skin, cut into strips or left in quarters, and the green olives. Continue cooking uncovered for 20 to 30 minutes until the sauce reduces to a thick, oily, intensely flavoured gravy.' },
      { type: 'p', text: 'Taste before salting. The preserved lemons will have already seasoned the dish substantially; you may need no additional salt at all. Discard the herb bundle.' },
      { type: 'p', text: 'Serve directly from the tagine with a large amount of bread. Tear and dip. No cutlery necessary or appropriate.' },
      { type: 'h2', text: 'On preserved lemons' },
      { type: 'p', text: 'The lemons our chef uses have been preserved for at least a year, sometimes two, in salt and their own juice. They are a completely different product from the jars marketed as preserved lemons in most supermarkets. If you can find a North African grocer, buy theirs. If not, make your own at least three months before you plan to cook this dish.' },
      { type: 'h2', text: 'On patience' },
      { type: 'p', text: 'Our chef told me the key to this tagine is patience. She meant it literally: low heat, long time. But she also clearly meant it in every other sense. Forty-five minutes of impatient cooking will produce something edible but flat. An hour and a half of genuine low heat produces the sauce that guests eat in complete silence, which is the correct response to it.' },
      { type: 'note', text: 'We serve this on the fourth evening of every retreat, preceded by an afternoon at the hammam. The sequencing is intentional: a softened body receives food differently.' },
    ],
  },
  {
    id: 'why-we-run-small-groups',
    n: '06',
    cat: 'Dispatch',
    date: 'Jan 2026',
    isoDate: '2026-01-12',
    title: 'Why we run small groups',
    excerpt: 'The retreats are capped at 12 people, not because of the riad\'s capacity, but because of what happens to a group when it crosses a certain size.',
    read: '4 min',
    img: 'uploads/20260317_094530.jpg',
    pos: 'center',
    metaDesc: 'Why Yogavibe yoga retreats in Essaouira are capped at 12 guests — the philosophy behind small-group retreats, better teaching, and the case for intimacy over scale.',
    body: [
      { type: 'p', text: 'The retreats are capped at 12 people. The riad could accommodate a few more. We choose not to.' },
      { type: 'p', text: 'This is sometimes the first question: smaller groups mean higher per-person costs. This is true and worth addressing directly.' },
      { type: 'h2', text: 'What happens at 14' },
      { type: 'p', text: 'Somewhere between 12 and 14 people, a group stops behaving like a group and starts behaving like a crowd. Subgroups form. The quieter guests retreat into themselves. The energy on the mat fragments: some people are in the class, some are watching the class, some have already mentally moved on to lunch. The shared meals that should feel like a long table at someone\'s home start to feel like a restaurant.' },
      { type: 'p', text: 'I noticed this running retreats for tour operators in 2016 and 2017, when groups were regularly 20 to 25 people. Technically functional. Often lonely in ways that were difficult to pinpoint but impossible to ignore.' },
      { type: 'h2', text: 'What changes at 10' },
      { type: 'p', text: 'With 10 people, everyone knows everyone by the second morning. Not deeply — not yet — but enough to offer a yoga block to the person next to them without being asked. Enough to sit next to the guest eating alone at lunch without making it a thing. The social friction of strangers dissolves faster in a small group, and what remains is the actual retreat: the practice, the rest, the conversations that begin over dinner and continue until late on the terrace.' },
      { type: 'h2', text: 'Teaching is different' },
      { type: 'p', text: 'Teaching 10 people is fundamentally different from teaching 20. With 10, I can see every body in the room. I can offer a specific adjustment to the guest who needs a hip-flexor modification rather than a generic cue to the whole class. The person who needs to be left alone gets left alone. The person who needs to be challenged is challenged. This is the difference between a class and a practice — and it requires a small group to achieve.' },
      { type: 'p', text: 'We also run two teachers on every session. In a group of 12, one leads, one adjusts. No one falls through the gaps.' },
      { type: 'h2', text: 'On the cost' },
      { type: 'p', text: 'Retreats at Yogavibe cost more per person than large-group alternatives. The fixed costs — riad takeover, two teachers, daily catering, transfers and activities — are shared across fewer guests, and we do not add guests to reduce the per-person cost. The price reflects the experience we want to deliver, not the margin we want to achieve.' },
      { type: 'p', text: 'Guests who come once tend to come back. We take this as evidence that the trade-off is the right one.' },
      { type: 'note', text: 'Custom retreats for companies, friend groups and yoga teacher collectives are available for groups of 8 to 18. Full riad takeover, custom schedule. If you want to talk through what this could look like for your group, reach out: charlotte@yogavibe.eu.' },
    ],
  },
];

/* ============ JOURNAL LIST PAGE ============ */
function JournalPage({ setPage, setJournalPost }) {
  return (
    <>
      <section className="section" style={{ paddingTop: 160 }}>
        <div className="container">
          <div className="caption">Yogavibe / Journal</div>
          <h1 className="h-display" style={{ marginTop: 24, maxWidth: 1100 }}>
            Notes from <em>the road.</em>
          </h1>
          <p className="lead" style={{ marginTop: 36, maxWidth: 520 }}>
            Recipes from the riad kitchen, playlists from morning practice, reading lists, and dispatches between retreats.
          </p>
        </div>
      </section>

      <section className="section-tight">
        <div className="container">
          <div className="journal-grid">
            {JOURNAL_POSTS.map((p, i) => (
              <article key={i} className="journal-post" onClick={() => { setJournalPost(p); setPage('journal-article'); }} style={{ cursor: 'pointer' }}>
                <div className="journal-img">
                  <Placeholder coords={p.cat.toUpperCase()} label={p.title} tag={p.n} src={p.img} pos={p.pos} ratio={4 / 5} />
                </div>
                <div style={{ marginTop: 20 }}>
                  <div className="row between">
                    <span className="caption">{p.cat} · {p.date}</span>
                    <span className="caption">{p.read} read</span>
                  </div>
                  <h2 className="h3" style={{ marginTop: 12, fontSize: 26 }}>{p.title}</h2>
                  <p style={{ marginTop: 10, fontSize: 14, color: 'var(--ink-soft)', lineHeight: 1.65 }}>{p.excerpt}</p>
                </div>
              </article>
            ))}
          </div>
        </div>
      </section>
    </>
  );
}

/* ============ JOURNAL ARTICLE PAGE ============ */
function JournalArticlePage({ post, setPage }) {
  /* Use the shared SEO hook so title, description, canonical, and OG tags
     all update consistently for each article. */
  usePageSEO(post ? {
    title: `${post.title} — Yogavibe Journal`,
    description: post.metaDesc,
    canonical: `https://yogavibe.eu/journal/${post.id}`,
    ogType: 'article',
    ogImage: post.img && post.img.startsWith('http') ? post.img : `https://yogavibe.eu/${post.img}`,
  } : PAGE_SEO.journal);

  if (!post) {
    return (
      <section className="section" style={{ paddingTop: 160 }}>
        <div className="container">
          <p className="caption">Article not found.</p>
          <button className="btn btn-ghost" onClick={() => setPage('journal')} style={{ marginTop: 24 }}>← Back to journal</button>
        </div>
      </section>
    );
  }

  const currentIndex = JOURNAL_POSTS.findIndex(p => p.id === post.id);
  const prev = JOURNAL_POSTS[currentIndex + 1] || null;
  const next = JOURNAL_POSTS[currentIndex - 1] || null;

  return (
    <>
      <article itemScope itemType="https://schema.org/Article">
        <section className="section" style={{ paddingTop: 140 }}>
          <div className="container">
            <button className="caption back-link" onClick={() => setPage('journal')}>← Journal</button>

            <div className="journal-article-header">
              <div className="caption" style={{ marginTop: 32 }}>
                <span itemProp="articleSection">{post.cat}</span>
                {' · '}
                <time dateTime={post.isoDate} itemProp="datePublished">{post.date}</time>
                {' · '}
                {post.read} read
              </div>
              <h1 className="h-display" style={{ marginTop: 16, maxWidth: 860 }} itemProp="headline">{post.title}</h1>
              <p className="lead" style={{ marginTop: 24, maxWidth: 620 }} itemProp="description">{post.excerpt}</p>
              <div className="journal-article-byline" itemProp="author" itemScope itemType="https://schema.org/Person">
                <span className="caption">By <span itemProp="name">Charlotte Schoeters</span>, Yogavibe</span>
              </div>
            </div>
          </div>
        </section>

        <section className="section-tight" style={{ paddingBottom: 0 }}>
          <div className="container">
            <div className="journal-article-img">
              <Placeholder coords={post.cat.toUpperCase()} label={post.title} tag={post.n} src={post.img} pos={post.pos} ratio={16 / 9} />
            </div>
          </div>
        </section>

        <section className="section" style={{ paddingTop: 80 }}>
          <div className="container">
            <div className="journal-article-body" itemProp="articleBody">
              {post.body.map((block, i) => {
                if (block.type === 'p') return <p key={i} className="body" style={{ fontSize: 17, lineHeight: 1.8, marginBottom: 24, maxWidth: 680 }}>{block.text}</p>;
                if (block.type === 'h2') return <h2 key={i} className="h3" style={{ marginTop: 56, marginBottom: 20, maxWidth: 680 }}>{block.text}</h2>;
                if (block.type === 'h3') return <h3 key={i} style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 400, fontStyle: 'italic', marginTop: 32, marginBottom: 12, maxWidth: 680, color: 'var(--ink)' }}>{block.text}</h3>;
                if (block.type === 'ul') return (
                  <ul key={i} style={{ maxWidth: 680, marginBottom: 24, paddingLeft: 0, listStyle: 'none' }}>
                    {block.items.map((item, j) => (
                      <li key={j} style={{ fontSize: 16, lineHeight: 1.7, padding: '8px 0 8px 20px', borderBottom: '1px dashed var(--line)', position: 'relative', color: 'var(--ink-soft)' }}>
                        <span style={{ position: 'absolute', left: 0, top: 13, width: 8, height: 1, background: 'var(--accent)', display: 'block' }}></span>
                        {item}
                      </li>
                    ))}
                  </ul>
                );
                if (block.type === 'note') return (
                  <div key={i} style={{ maxWidth: 680, margin: '40px 0', padding: '24px 28px', borderLeft: '2px solid var(--accent)', background: 'var(--bg-alt)', fontSize: 15, lineHeight: 1.75, color: 'var(--ink-soft)', fontStyle: 'italic' }}>
                    {block.text}
                  </div>
                );
                if (block.type === 'quote') return (
                  <blockquote key={i} style={{ maxWidth: 680, margin: '48px 0', padding: '0 0 0 32px', borderLeft: '2px solid var(--accent)' }}>
                    <p style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(22px, 3vw, 30px)', fontStyle: 'italic', lineHeight: 1.4, color: 'var(--ink)' }}>{block.text}</p>
                  </blockquote>
                );
                return null;
              })}
            </div>
          </div>
        </section>
      </article>

      {/* More from the journal */}
      <section className="section" style={{ borderTop: '1px solid var(--line)', paddingTop: 80 }}>
        <div className="container">
          <div className="caption" style={{ marginBottom: 40 }}>More from the journal</div>
          <div className="journal-article-nav">
            {next && (
              <div className="journal-article-nav-item" onClick={() => { setJournalPost(next); window.scrollTo({ top: 0, behavior: 'instant' }); }} style={{ cursor: 'pointer' }}>
                <div className="caption" style={{ marginBottom: 10 }}>← Newer</div>
                <span className="caption" style={{ color: 'var(--ink-soft)' }}>{next.cat} · {next.date}</span>
                <div className="h3" style={{ marginTop: 8, fontSize: 22 }}>{next.title}</div>
              </div>
            )}
            {prev && (
              <div className="journal-article-nav-item" style={{ textAlign: 'right', cursor: 'pointer' }} onClick={() => { setJournalPost(prev); window.scrollTo({ top: 0, behavior: 'instant' }); }}>
                <div className="caption" style={{ marginBottom: 10 }}>Older →</div>
                <span className="caption" style={{ color: 'var(--ink-soft)' }}>{prev.cat} · {prev.date}</span>
                <div className="h3" style={{ marginTop: 8, fontSize: 22 }}>{prev.title}</div>
              </div>
            )}
          </div>
          <div style={{ marginTop: 48 }}>
            <button className="btn btn-ghost" onClick={() => setPage('journal')}>← All articles</button>
          </div>
        </div>
      </section>
    </>
  );
}

function setJournalPost(post) {} // placeholder — overridden by App

Object.assign(window, { JournalPage, JournalArticlePage, JOURNAL_POSTS });
