<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta property="og:description" content="Notes from the studio — process, exhibitions, and works in progress."><meta name="description" content="Notes from the studio — process, exhibitions, and works in progress."><meta name="twitter:description" content="Notes from the studio — process, exhibitions, and works in progress."><meta property="og:title" content="Blog | Melissa Cameron Jewellery"><meta name="twitter:title" content="Blog | Melissa Cameron Jewellery"><meta property="og:image" content="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-scaled.jpg"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="twitter:image" content="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-scaled.jpg"><meta property="og:site_name" content="Melissa Cameron Jewellery"><meta property="og:locale" content="en_AU"><link rel="canonical" href="https://www.melissacameron.net/blog"><meta property="og:url" content="https://www.melissacameron.net/blog"><meta name="twitter:card" content="summary_large_image"><title>Blog | Melissa Cameron Jewellery</title><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://www.melissacameron.net/#website","url":"https://www.melissacameron.net/","name":"Melissa Cameron Jewellery","publisher":{"@id":"https://www.melissacameron.net/#person"},"inLanguage":"en-AU"},{"@type":"Person","@id":"https://www.melissacameron.net/#person","name":"Melissa Cameron","url":"https://www.melissacameron.net/","jobTitle":"Jeweller","sameAs":["https://www.instagram.com/melissacameronjeweller/","https://www.melissacameron.net/blog/"]}]}</script><link rel="icon" href="/favicon.ico" sizes="32x32" type="image/x-icon"><link rel="preload" href="/styles/fonts/sen_regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"><link rel="alternate" type="application/rss+xml" title="Melissa Cameron Jewellery — Journal" href="/blog/feed.xml"><link rel="stylesheet" href="/_astro/GalleryLayout.Be3X263V.css"></head> <body> <a href="#main" class="skip-link">Skip to main content</a> <nav class="gnav" aria-label="Primary navigation" data-pagefind-ignore> <a class="gnav-logo" href="/" aria-label="Melissa Cameron — home"> <span class="dot" aria-hidden="true"></span> <span>Melissa Cameron</span> </a> <div class="gnav-links desktop-only" role="menubar"> <a class="gnav-link" href="/series">Series</a> <a class="gnav-link" href="/exhibitions">Exhibitions</a> <a class="gnav-link active" href="/blog">Blog</a> <a class="gnav-link" href="/cv">CV</a> <a class="gnav-link" href="/page/about">About</a> </div> <div class="gnav-right"> <button class="gnav-icon gnav-search-btn" type="button" id="site-search-open" aria-label="Search" aria-haspopup="dialog" aria-controls="site-search"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="11" cy="11" r="7"></circle> <line x1="20" y1="20" x2="16.5" y2="16.5"></line> </svg> </button> <a class="gnav-icon" href="https://www.instagram.com/melissacameronjeweller/" target="_blank" rel="noopener noreferrer" aria-label="Instagram"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="3" y="3" width="18" height="18" rx="5"></rect> <circle cx="12" cy="12" r="4"></circle> <circle cx="17.5" cy="6.5" r="0.8" fill="currentColor"></circle> </svg> </a> <a class="gnav-icon" href="mailto:jewellery@melissacameron.net" aria-label="Email"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="3" y="5" width="18" height="14" rx="2"></rect> <polyline points="3 7 12 13 21 7"></polyline> </svg> </a> <a class="gnav-icon" href="/cart" aria-label="Cart"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 7h14l-1.5 9.5a2 2 0 0 1-2 1.7H8.5a2 2 0 0 1-2-1.7L5 7Z"></path> <path d="M9 7V5a3 3 0 1 1 6 0v2"></path> </svg> </a> <button class="gnav-burger" type="button" aria-label="Open menu" aria-controls="gnav-menu" aria-expanded="false"> <span></span><span></span><span></span> </button> </div> </nav> <div class="gmenu" id="gnav-menu" role="dialog" aria-modal="true" aria-label="Site menu" hidden data-pagefind-ignore> <div class="gmenu-inner"> <button class="gmenu-close" type="button" aria-label="Close menu"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <line x1="6" y1="6" x2="18" y2="18"></line><line x1="18" y1="6" x2="6" y2="18"></line> </svg> </button> <div class="gmenu-list"> <a href="/series">Series</a> <a href="/exhibitions">Exhibitions</a> <a href="/blog">Blog</a> <a href="/cv">CV</a> <a href="/page/about">About</a> <a href="/cart">Cart</a> </div> <div class="gmenu-footer"> <span class="mono">North Perth, Australia</span> </div> </div> </div> <script>
  (() => {
    const burger = document.querySelector(".gnav-burger");
    const menu = document.getElementById("gnav-menu");
    const close = menu?.querySelector(".gmenu-close");
    if (!burger || !menu || !close) return;

    const setOpen = (open) => {
      menu.classList.toggle("open", open);
      menu.hidden = !open;
      burger.setAttribute("aria-expanded", String(open));
      document.documentElement.style.overflow = open ? "hidden" : "";
    };

    burger.addEventListener("click", () => setOpen(!menu.classList.contains("open")));
    close.addEventListener("click", () => setOpen(false));
    menu.addEventListener("click", (e) => {
      if (e.target === menu) setOpen(false);
    });
    document.addEventListener("keydown", (e) => {
      if (e.key === "Escape" && menu.classList.contains("open")) {
        setOpen(false);
        burger.focus();
      }
    });
  })();
</script> <main id="main" tabindex="-1">  <section class="index-page fade-in"> <header class="index-head"> <div> <h1 class="tight">Journal</h1> <p class="lede">Notes from the studio — process, exhibitions, and works in progress.</p> </div> <div class="right"> <span class="count">1095 entries</span> <a class="count rss-link" href="/blog/feed.xml" type="application/rss+xml" aria-label="RSS feed"> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 11a9 9 0 0 1 9 9"></path> <path d="M4 4a16 16 0 0 1 16 16"></path> <circle cx="5" cy="19" r="1"></circle> </svg> <span>RSS</span> </a> <a class="count rss-link" href="/blog/wp-admin" rel="nofollow" aria-label="Blog admin"> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="12" cy="12" r="3"></circle> <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path> </svg> <span>Admin</span> </a> </div> </header> <div class="blog-grid"> <a class="card" href="/blog/close-reading"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-600x600.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-440x440.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-600x600.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-768x768.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-1536x1536.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-2048x2048.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-2400x2400.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/09/805-scaled.jpg 2560w" sizes="(min-width: 1280px) 475px, (min-width: 1100px) 42vw, (min-width: 720px) 63vw, 125vw" alt width="400" height="500" loading="eager" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Close Reading</span> <span class="card-sub">04 Sept 2026</span> </div> <p class="card-blurb">This afternoon from 2pm I’ll be sharing the beautiful stage set by Meredith Turnbull and Ross Coulter at Alta Forma in Naarm, with my fellow artists Minhi…</p> </a><a class="card" href="/blog/jarrah-chronicles"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-600x475.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-440x348.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-600x475.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-768x608.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-1536x1217.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-2048x1622.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-2400x1901.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/08/jarrah-scaled.jpg 2560w" sizes="(min-width: 1280px) 600px, (min-width: 1100px) 53vw, (min-width: 720px) 79vw, 158vw" alt width="400" height="500" loading="eager" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Jarrah chronicles</span> <span class="card-sub">10 Aug 2026</span> </div> <p class="card-blurb">Hello friends, If you are receiving this introduction email, you are a beloved recipient of a recycled jarrah offcut. This is the first email to announce…</p> </a><a class="card" href="/blog/community-for-ethical-jewelry"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-600x450.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-440x330.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-600x450.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-768x576.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-1536x1152.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-2048x1536.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-2400x1800.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/06/RJM-26-Haul-scaled.jpg 2560w" sizes="(min-width: 1280px) 634px, (min-width: 1100px) 55vw, (min-width: 720px) 84vw, 167vw" alt width="400" height="500" loading="eager" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Community for Ethical Jewelry</span> <span class="card-sub">15 June 2026</span> </div> <p class="card-blurb">In the Studio with Radical Jewelry Makeover: Webinar Series A couple of Fridays ago, I got up pre-dawn (just before 6am, AWST) to be online for the second in…</p> </a><a class="card" href="/blog/contemporary-jewellery-showcase-2026"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01-768x1024.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01-440x587.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01-450x600.jpg 450w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01-768x1024.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01-1152x1536.jpg 1152w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/CJS-2026_SKL01.jpg 1500w" sizes="(min-width: 1280px) 380px, (min-width: 1100px) 33vw, (min-width: 720px) 50vw, 100vw" alt width="400" height="500" loading="eager" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Contemporary Jewellery Showcase 2026</span> <span class="card-sub">22 Mar 2026</span> </div> <p class="card-blurb">Eep! We’re already half-way into this one-week superfast-superfun show at Shopfront Gallery at 149 Beaufort St in Boorloo/Perth. I’ve been all over Insta and…</p> </a><a class="card" href="/blog/jewellery-techniques-series"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-600x401.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-440x294.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-600x401.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-768x513.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-1536x1025.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-2048x1367.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-2400x1602.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/03/MC_Cu-Plastic_set_26-2-scaled.jpg 2560w" sizes="(min-width: 1280px) 712px, (min-width: 1100px) 62vw, (min-width: 720px) 94vw, 188vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Jewellery Techniques Series</span> <span class="card-sub">03 Mar 2026</span> </div> <p class="card-blurb">Midland Junction Arts Centre – Workshop Over two days learn specific tools and techniques to turn scrap copper and plastics into unique wearable jewels. In…</p> </a><a class="card" href="/blog/aotm-x-sick-af"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-600x400.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-440x293.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-600x400.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-768x512.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-1536x1023.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-2048x1364.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-2400x1599.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-Collab-Recycled-CuPE-25-sml-scaled.jpg 2560w" sizes="(min-width: 1280px) 714px, (min-width: 1100px) 62vw, (min-width: 720px) 94vw, 188vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">AOTM x Sick AF</span> <span class="card-sub">25 Feb 2026</span> </div> <p class="card-blurb">Oh yeah, it’s time! TOMORROW NIGHT, 27th of Feb 2026, Sick AF the touring exhibition will be launched in Kepa Kurl / Esperance at the Cannery Arts Centre. The…</p> </a><a class="card" href="/blog/melbourne-art-fair"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-768x1154.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-399x600.jpg 399w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-440x661.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-768x1154.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-1022x1536.jpg 1022w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-1363x2048.jpg 1363w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-1597x2400.jpg 1597w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/02/MC-RCW-5-photo-Ross-Coulter-scaled.jpg 1703w" sizes="(min-width: 1280px) 380px, (min-width: 1100px) 33vw, (min-width: 720px) 50vw, 100vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Melbourne Art Fair!</span> <span class="card-sub">20 Feb 2026</span> </div> <p class="card-blurb">Late notice, I know, but… Alta Forma is part of FUTUREOBJEKT at Melbourne Art Fair! Alta Forma is thrilled to present Roseanne Bartley, Sam Blomley and Gilda…</p> </a><a class="card" href="/blog/jewellery-tv"> <div class="card-img card-img-empty"> <div class="card-img-fallback" aria-hidden="true"> <span class="card-img-date mono">28 Jan 2026</span> <span class="card-img-mark" aria-hidden="true"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="0.8" stroke-linecap="round"> <path d="M10 14h20M10 20h20M10 26h12"></path> </svg> </span> </div> </div> <div class="card-meta"> <span class="card-title tight">Jewellery TV</span> <span class="card-sub">28 Jan 2026</span> </div> <p class="card-blurb">Hey there reader, it’s a very occasional YouTube roundup. I have Adam Savage first, with a bunch of jeweller-related material in his 2025 end-of-year roundup.…</p> </a><a class="card" href="/blog/happy-2026"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-600x400.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-440x294.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-600x400.jpg 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-768x513.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-1536x1025.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-2048x1367.jpg 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-2400x1602.jpg 2400w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2026/01/Marfa-Boxed-scaled.jpg 2560w" sizes="(min-width: 1280px) 712px, (min-width: 1100px) 62vw, (min-width: 720px) 94vw, 188vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">Happy 2026 :)</span> <span class="card-sub">13 Jan 2026</span> </div> <p class="card-blurb">Hello! We’ve made it though the back end of ’25 and into a new year, with global shenanigans barrelling on (oohhh, barrelling, like oil?) at a pace that 2020…</p> </a><a class="card" href="/blog/open-studio-2025"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-768x1024.jpg" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-440x587.jpg 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-450x600.jpg 450w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-768x1024.jpg 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-1152x1536.jpg 1152w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-1536x2048.jpg 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-1800x2400.jpg 1800w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/251125_Vittino-Ashe_41-scaled.jpg 1920w" sizes="(min-width: 1280px) 380px, (min-width: 1100px) 33vw, (min-width: 720px) 50vw, 100vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">open studio 2025</span> <span class="card-sub">15 Dec 2025</span> </div> <p class="card-blurb">Y’all, we did it, we’re in! Please come help us celebrate the launch of the new house and studio, and the good friends who made it all happen. Katherine and…</p> </a><a class="card" href="/blog/moving"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing-600x188.png" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing-440x138.png 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing-600x188.png 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing-768x241.png 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing-1536x482.png 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/12/furniture-listing.png 1722w" sizes="(min-width: 1280px) 1515px, (min-width: 1100px) 132vw, (min-width: 720px) 200vw, 399vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">&#8230;moving</span> <span class="card-sub">04 Dec 2025</span> </div> <p class="card-blurb">Y’all. It’s happening. Some of you have pieces of recycled jarrah that I’ve been passing around the country; these are jewellery-scale offcuts from our build.…</p> </a><a class="card" href="/blog/iota-craft-co-lab-symposium"> <div class="card-img"> <img src="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-600x300.webp" srcset="https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-440x220.webp 440w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-600x300.webp 600w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-768x384.webp 768w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-1536x768.webp 1536w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB-2048x1024.webp 2048w, https://s3.ap-southeast-2.amazonaws.com/melissacameron.net-blogcontentau/uploads/2025/10/CRAFT-CO-LAB.webp 2400w" sizes="(min-width: 1280px) 950px, (min-width: 1100px) 83vw, (min-width: 720px) 125vw, 250vw" alt width="400" height="500" loading="lazy" decoding="async"> </div> <div class="card-meta"> <span class="card-title tight">IOTA Craft Co-lab Symposium</span> <span class="card-sub">28 Oct 2025</span> </div> <p class="card-blurb">Team! The IOTA Curatorium has been assembled, and they are CURRENTLY IN BOORLOO (Perth) checking out the neighbourhood, in advance of making some great calls…</p> </a> </div> <nav class="blog-pagination" aria-label="More entries"> <span></span> <a href="/blog/archive" class="mono">View all 1095 entries →</a> </nav> </section>  </main>  <footer class="gfooter" data-pagefind-ignore> <div class="row"> <span class="name">Melissa Cameron</span> <span class="mono">North Perth, Australia</span> <span class="mono">© 2026</span> </div> <div class="row"> <a href="/page/terms" class="mono">Terms</a> <a href="/page/privacy" class="mono">Privacy</a> <a href="/page/shipping" class="mono">Shipping</a> <a href="https://www.instagram.com/melissacameronjeweller/" class="mono" target="_blank" rel="noopener noreferrer">Instagram</a> <a href="mailto:jewellery@melissacameron.net" class="mono">Email</a> </div> <p class="acknowledge">
We acknowledge Aboriginal and Torres Strait Islander peoples as the Traditional
    Custodians of the lands, waters and seas where we live and work. We pay our
    respects to their Elders and to all Traditional Owners/Custodians and acknowledge
    that sovereignty was never ceded.
</p> </footer> <div class="site-search" id="site-search" role="dialog" aria-modal="true" aria-label="Search the site" hidden data-pagefind-ignore> <div class="site-search-backdrop"></div> <div class="site-search-panel"> <div class="site-search-input-row"> <svg class="site-search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="11" cy="11" r="7"></circle> <line x1="20" y1="20" x2="16.5" y2="16.5"></line> </svg> <input type="search" id="site-search-input" placeholder="Search works, series, posts…" autocomplete="off" spellcheck="false" aria-label="Search query"> <button class="site-search-close" type="button" aria-label="Close search"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <line x1="6" y1="6" x2="18" y2="18"></line> <line x1="18" y1="6" x2="6" y2="18"></line> </svg> </button> </div> <div class="site-search-status" id="site-search-status" aria-live="polite"></div> <ol class="site-search-results" id="site-search-results"></ol> </div> </div> <script>
  (() => {
    const panel = document.getElementById("site-search");
    if (!panel) return;
    // Open button lives in GalleryNav (SearchButton.astro). On pages
    // with hideNav={true} the button isn't rendered — the overlay is
    // still openable via the `/` and Cmd/Ctrl-K shortcuts.
    const openBtn = document.getElementById("site-search-open");
    const input = panel.querySelector("#site-search-input");
    const closeBtn = panel.querySelector(".site-search-close");
    const backdrop = panel.querySelector(".site-search-backdrop");
    const list = panel.querySelector("#site-search-results");
    const status = panel.querySelector("#site-search-status");

    let pagefindPromise = null;
    // Lazy-load /pagefind/pagefind.js on first open so we don't pay for
    // the index loader on pages a user never searches from. The static
    // import path is computed per-host because dynamic imports resolve
    // relative to the document, not this script — same origin always.
    const loadPagefind = () => {
      if (!pagefindPromise) {
        pagefindPromise = import("/pagefind/pagefind.js")
          .then(async (m) => {
            await m.options({ excerptLength: 30 });
            return m;
          })
          .catch((err) => { console.warn("[search] pagefind load failed", err); return null; });
      }
      return pagefindPromise;
    };

    const setOpen = (open) => {
      panel.hidden = !open;
      panel.classList.toggle("open", open);
      document.documentElement.style.overflow = open ? "hidden" : "";
      if (open) {
        loadPagefind();
        // Microtask delay lets the [hidden] removal commit so the input
        // is actually focusable when we call .focus().
        requestAnimationFrame(() => input.focus());
      } else {
        input.value = "";
        teardownSentinel();
        currentResults = [];
        renderedCount = 0;
        appendInFlightToken = 0;
        list.innerHTML = "";
        status.textContent = "";
      }
    };

    openBtn?.addEventListener("click", () => setOpen(true));
    closeBtn?.addEventListener("click", () => setOpen(false));
    backdrop?.addEventListener("click", () => setOpen(false));
    document.addEventListener("keydown", (e) => {
      if (e.key === "Escape" && !panel.hidden) {
        setOpen(false);
        openBtn?.focus();
      } else if ((e.key === "/" || (e.key === "k" && (e.metaKey || e.ctrlKey))) && panel.hidden) {
        // Don't hijack typing inside other inputs.
        const t = e.target;
        if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
        e.preventDefault();
        setOpen(true);
      }
    });

    // Coalesce keystrokes so we don't run a fresh pagefind search on
    // every character — debounce 80ms; below that the index lookup is
    // fast enough that "instant" feels right.
    const PAGE_SIZE = 12;
    let queryToken = 0;
    let debounceTimer = null;
    let currentResults = [];
    let renderedCount = 0;
    // Track the token currently fetching a page rather than a bare bool.
    // With a bool, a stale appendNextPage from query N still mid-await
    // would block the next query's first appendNextPage call ("already
    // in flight"), and the stale call would then no-op out via the
    // token check — leaving the status showing the new query's count
    // but the list empty.
    let appendInFlightToken = 0;
    let sentinelObserver = null;

    const renderRow = (f) => {
      const li = document.createElement("li");
      li.className = "site-search-row";
      const type = (f.meta && f.meta.type) || "Page";
      const img = f.meta && f.meta.image
        ? `<img class="site-search-thumb" src="${f.meta.image}" alt="" loading="lazy" decoding="async" width="44" height="44">`
        : `<span class="site-search-thumb placeholder" aria-hidden="true"></span>`;
      // The excerpt is HTML with <mark> highlights from pagefind —
      // safe to inject because pagefind escapes the source content.
      li.innerHTML =
        `<a href="${f.url}" class="site-search-link">` +
          img +
          `<div class="site-search-text">` +
            `<div class="site-search-row-head">` +
              `<span class="site-search-type">${type}</span>` +
              `<span class="site-search-title">${f.meta && f.meta.title ? f.meta.title : f.url}</span>` +
            `</div>` +
            `<div class="site-search-excerpt">${f.excerpt}</div>` +
          `</div>` +
        `</a>`;
      return li;
    };

    const updateStatus = () => {
      if (currentResults.length === 0) {
        status.textContent = "";
        return;
      }
      // NN / MM — current rendered count over total. Padded to a stable
      // width so the digits don't shift as we scroll.
      const total = currentResults.length;
      const pad = String(total).length;
      const nn = String(renderedCount).padStart(pad, "0");
      status.textContent = `${nn} / ${total}`;
    };

    // Append the next page of fragments to the list. Token-guarded so a
    // stale append from a previous query never lands in the new results.
    const appendNextPage = async (token) => {
      // Already loading a page for THIS query? Skip. A stale token left
      // pending from a previous query no longer gates new query progress.
      if (appendInFlightToken === token) return;
      if (renderedCount >= currentResults.length) return;
      appendInFlightToken = token;
      setSentinelLoading(true);
      try {
        const slice = currentResults.slice(renderedCount, renderedCount + PAGE_SIZE);
        const fragments = await Promise.all(slice.map((r) => r.data()));
        if (token !== queryToken) return;
        for (const f of fragments) list.appendChild(renderRow(f));
        renderedCount += slice.length;
        updateStatus();
        // Re-attach sentinel at the new tail so the IntersectionObserver
        // continues firing as the user scrolls. Tearing it down + recreating
        // is cheaper than juggling a moved observer target.
        teardownSentinel();
        if (renderedCount < currentResults.length) {
          attachSentinel(token);
        } else {
          showEndMarker();
        }
      } finally {
        // Only clear our own loading state — a stale finally must not
        // wipe out the in-flight flag/spinner belonging to a newer query
        // that started while we were awaiting.
        if (appendInFlightToken === token) {
          appendInFlightToken = 0;
          setSentinelLoading(false);
        }
      }
    };

    // Toggle the loading visual on the current sentinel. Inline spinner +
    // "Loading more…" label so the user sees the next batch is on the way
    // even when the network roundtrip + decode finishes too fast for a
    // skeleton row to be worth the complexity.
    const setSentinelLoading = (loading) => {
      const sentinel = list.querySelector(".site-search-sentinel");
      if (!sentinel) return;
      sentinel.classList.toggle("loading", loading);
      sentinel.innerHTML = loading
        ? '<span class="site-search-spinner" aria-hidden="true"></span><span>Loading more…</span>'
        : "";
    };

    // End-of-results cap. Tells the user there's nothing further so they
    // don't keep scroll-fishing. Replaces the sentinel — once exhausted,
    // we don't need the observer any more.
    const showEndMarker = () => {
      const end = document.createElement("li");
      end.className = "site-search-end";
      end.setAttribute("aria-hidden", "true");
      end.textContent = `End of ${currentResults.length} result${currentResults.length === 1 ? "" : "s"}`;
      list.appendChild(end);
    };

    const teardownSentinel = () => {
      if (sentinelObserver) {
        sentinelObserver.disconnect();
        sentinelObserver = null;
      }
      const old = list.querySelector(".site-search-sentinel");
      if (old) old.remove();
      const oldEnd = list.querySelector(".site-search-end");
      if (oldEnd) oldEnd.remove();
    };

    const attachSentinel = (token) => {
      const sentinel = document.createElement("li");
      sentinel.className = "site-search-sentinel";
      sentinel.setAttribute("aria-hidden", "true");
      list.appendChild(sentinel);
      // rootMargin pulls the trigger ~200 px before the sentinel actually
      // intersects so the next batch is loaded before the user hits the
      // bottom — feels continuous instead of stuttering.
      sentinelObserver = new IntersectionObserver(
        (entries) => {
          if (entries.some((e) => e.isIntersecting)) appendNextPage(token);
        },
        { root: list, rootMargin: "200px" },
      );
      sentinelObserver.observe(sentinel);
    };

    // Belt-and-braces: even with the sentinel observer, some browsers
    // skip the initial "already in view" callback when an empty <li>
    // has zero intrinsic height. A direct scroll-position check fires
    // reliably whenever the user scrolls past the threshold.
    list.addEventListener("scroll", () => {
      if (renderedCount === 0 || renderedCount >= currentResults.length) return;
      const remaining = list.scrollHeight - (list.scrollTop + list.clientHeight);
      if (remaining < 240) appendNextPage(queryToken);
    });

    const runSearch = async (q) => {
      const myToken = ++queryToken;
      const trimmed = q.trim();
      teardownSentinel();
      currentResults = [];
      renderedCount = 0;
      if (!trimmed) {
        list.innerHTML = "";
        status.textContent = "";
        return;
      }
      status.textContent = "Searching…";
      const pagefind = await loadPagefind();
      if (myToken !== queryToken) return; // user typed more
      if (!pagefind) {
        status.textContent = "Search index unavailable.";
        return;
      }
      const search = await pagefind.search(trimmed);
      if (myToken !== queryToken) return;
      if (search.results.length === 0) {
        list.innerHTML = "";
        status.textContent = `No results for “${trimmed}”.`;
        return;
      }
      currentResults = search.results;
      list.innerHTML = "";
      updateStatus(); // sets `0 / MM` until the first batch lands
      await appendNextPage(myToken);
    };

    input?.addEventListener("input", (e) => {
      clearTimeout(debounceTimer);
      const v = e.target.value;
      debounceTimer = setTimeout(() => runSearch(v), 80);
    });
  })();
</script> <script async src="https://www.googletagmanager.com/gtag/js?id=G-PVKPPS9MMY"></script> <script>(function(){const tag = "G-PVKPPS9MMY";

  window.dataLayer = window.dataLayer || [];
  function gtag() { dataLayer.push(arguments); }
  gtag("js", new Date());
  gtag("config", tag);
})();</script> </body></html>