diff --git a/htb2trilium/htb2trilium_challenges.py b/htb2trilium/htb2trilium_challenges.py
index d0d95ca..4babcba 100644
--- a/htb2trilium/htb2trilium_challenges.py
+++ b/htb2trilium/htb2trilium_challenges.py
@@ -1,6 +1,8 @@
 import os
 import json
 import requests
+import re
+import unicodedata
 from collections import defaultdict
 from bs4 import BeautifulSoup
 from datetime import datetime, timezone
@@ -53,6 +55,15 @@
 
 print(f"[i] Retrieved {len(challenges)} challenges")
 
+def normalise_title(title):
+    # Strip accents, smart quotes, etc.
+    title = unicodedata.normalize("NFKD", title)
+    # Replace fancy punctuation
+    title = title.replace("’", "'").replace("`", "'").replace("–", "-").replace("“", '"').replace("”", '"')
+    # Remove excess whitespace
+    title = re.sub(r'\s+', ' ', title)
+    return title.strip().lower()
+
 # Group challenges by their categories
 for challenge in challenges:
     categories[challenge['category_name']].append(challenge)
@@ -65,7 +76,7 @@
     completed = sum(1 for challenge in grouped_challenges if challenge['is_owned'])  # Count completed challenges
     
     res = ea.search_note(
-        search=f"note.title %= '{category}*'",
+        search=f"note.title %= '^{category}*'",
         ancestorNoteId=trilium_challenges_folder,
         ancestorDepth='eq1',
         limit=1,
@@ -73,52 +84,62 @@
     )
     
     catId = ""
-    if res['results'] and res['results'][0]['title'].split(' - ')[0].strip().lower() == category.lower():
-        # page exists - lets check if the details have changed
-        ea.patch_note(noteId=res['results'][0]['noteId'], title=category+" - "+str(completed)+" / "+str(total))
-        catId = res['results'][0]['noteId']
-        print(f"[i] updated category: {category} - ({completed}/{total})")
+    if res['results']:
+        matched_note = res['results'][0]
+        existing_category = " - ".join(matched_note['title'].split(" - ")[:-1])  # Extract category portion
+        if existing_category.strip().lower() == category.lower():
+            ea.patch_note(
+                noteId=matched_note['noteId'], 
+                title=f"{category} - {completed} / {total}"
+            )
+            catId = matched_note['noteId']
+            print(f"[i] Updated category: {category} - ({completed}/{total})")
     else:
         new_note = ea.create_note(
             parentNoteId=trilium_challenges_folder,
             type="text",
-            title=category+" - "+str(completed)+" / "+str(total),
+            title=f"{category} - {completed} / {total}",
             content=" ",
         )
         catId = new_note['note']['noteId']
-        print(f"[+] created category: {catId} {category} - ({completed}/{total})")
+        print(f"[+] Created category: {catId} {category} - ({completed}/{total})")
     
     for challenge in grouped_challenges:
         #print(f" - ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
+        escaped_name = challenge['name'].replace("'", "\\'").replace(",", "\\,")
         res2 = ea.search_note(
-            search=f"{challenge['name']}",
+            search=f"note.title = '{escaped_name}'",
             ancestorNoteId=catId,
             ancestorDepth='eq1',
+            orderBy=["title"],
             limit=1,
             fastSearch=True,
         )
-        # already exists update the values
-        if res2['results'] and res2['results'][0]['title'].lower() == challenge['name'].lower():
-            print(f"[i] found ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
-            #print(f"Search response for challenge '{challenge['name']}': {res2}")
-            for attribute in res2['results'][0]['attributes']:
-                if attribute['name'] == "Difficulty":
-                    ea.patch_attribute(attributeId=attribute['attributeId'], value=challenge['difficulty'])
-                if attribute['name'] == "Released":
-                    release_str = challenge['release_date']
-                    release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ")
-                    formatted_release_date = release_date.strftime("%d %B %Y")
-                    ea.patch_attribute(attributeId=attribute['attributeId'], value=formatted_release_date)
-                if attribute['name'] == "Solved":
-                    if challenge['is_owned']:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
-                    else:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value=" ")
-                if attribute['name'] == "cssClass":
-                    if challenge['is_owned']:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
-                    else:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
+
+        if res2['results']:
+            note_title = normalise_title(res2['results'][0]['title'])
+            challenge_title = normalise_title(challenge['name'])
+
+            if note_title == challenge_title:
+
+                for attribute in res2['results'][0]['attributes']:
+                    if attribute['name'] == "Difficulty":
+                        ea.patch_attribute(attributeId=attribute['attributeId'], value=challenge['difficulty'])
+                    if attribute['name'] == "Released":
+                        release_str = challenge['release_date']
+                        release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ")
+                        formatted_release_date = release_date.strftime("%d %B %Y")
+                        ea.patch_attribute(attributeId=attribute['attributeId'], value=formatted_release_date)
+                    if attribute['name'] == "Solved":
+                        if challenge['is_owned']:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
+                        else:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value=" ")
+                    if attribute['name'] == "cssClass":
+                        if challenge['is_owned']:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
+                        else:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
 
         else: # doesnt already exist, create page
             release_str = challenge['release_date']

diff --git a/htb2trilium/htb2trilium_challenges.py b/htb2trilium/htb2trilium_challenges.py
index d0d95ca..4babcba 100644
--- a/htb2trilium/htb2trilium_challenges.py
+++ b/htb2trilium/htb2trilium_challenges.py
@@ -1,6 +1,8 @@
 import os
 import json
 import requests
+import re
+import unicodedata
 from collections import defaultdict
 from bs4 import BeautifulSoup
 from datetime import datetime, timezone
@@ -53,6 +55,15 @@
 
 print(f"[i] Retrieved {len(challenges)} challenges")
 
+def normalise_title(title):
+    # Strip accents, smart quotes, etc.
+    title = unicodedata.normalize("NFKD", title)
+    # Replace fancy punctuation
+    title = title.replace("’", "'").replace("`", "'").replace("–", "-").replace("“", '"').replace("”", '"')
+    # Remove excess whitespace
+    title = re.sub(r'\s+', ' ', title)
+    return title.strip().lower()
+
 # Group challenges by their categories
 for challenge in challenges:
     categories[challenge['category_name']].append(challenge)
@@ -65,7 +76,7 @@
     completed = sum(1 for challenge in grouped_challenges if challenge['is_owned'])  # Count completed challenges
     
     res = ea.search_note(
-        search=f"note.title %= '{category}*'",
+        search=f"note.title %= '^{category}*'",
         ancestorNoteId=trilium_challenges_folder,
         ancestorDepth='eq1',
         limit=1,
@@ -73,52 +84,62 @@
     )
     
     catId = ""
-    if res['results'] and res['results'][0]['title'].split(' - ')[0].strip().lower() == category.lower():
-        # page exists - lets check if the details have changed
-        ea.patch_note(noteId=res['results'][0]['noteId'], title=category+" - "+str(completed)+" / "+str(total))
-        catId = res['results'][0]['noteId']
-        print(f"[i] updated category: {category} - ({completed}/{total})")
+    if res['results']:
+        matched_note = res['results'][0]
+        existing_category = " - ".join(matched_note['title'].split(" - ")[:-1])  # Extract category portion
+        if existing_category.strip().lower() == category.lower():
+            ea.patch_note(
+                noteId=matched_note['noteId'], 
+                title=f"{category} - {completed} / {total}"
+            )
+            catId = matched_note['noteId']
+            print(f"[i] Updated category: {category} - ({completed}/{total})")
     else:
         new_note = ea.create_note(
             parentNoteId=trilium_challenges_folder,
             type="text",
-            title=category+" - "+str(completed)+" / "+str(total),
+            title=f"{category} - {completed} / {total}",
             content=" ",
         )
         catId = new_note['note']['noteId']
-        print(f"[+] created category: {catId} {category} - ({completed}/{total})")
+        print(f"[+] Created category: {catId} {category} - ({completed}/{total})")
     
     for challenge in grouped_challenges:
         #print(f" - ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
+        escaped_name = challenge['name'].replace("'", "\\'").replace(",", "\\,")
         res2 = ea.search_note(
-            search=f"{challenge['name']}",
+            search=f"note.title = '{escaped_name}'",
             ancestorNoteId=catId,
             ancestorDepth='eq1',
+            orderBy=["title"],
             limit=1,
             fastSearch=True,
         )
-        # already exists update the values
-        if res2['results'] and res2['results'][0]['title'].lower() == challenge['name'].lower():
-            print(f"[i] found ID: {challenge['id']}, Name: {challenge['name']}, Difficulty: {challenge['difficulty']}")
-            #print(f"Search response for challenge '{challenge['name']}': {res2}")
-            for attribute in res2['results'][0]['attributes']:
-                if attribute['name'] == "Difficulty":
-                    ea.patch_attribute(attributeId=attribute['attributeId'], value=challenge['difficulty'])
-                if attribute['name'] == "Released":
-                    release_str = challenge['release_date']
-                    release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ")
-                    formatted_release_date = release_date.strftime("%d %B %Y")
-                    ea.patch_attribute(attributeId=attribute['attributeId'], value=formatted_release_date)
-                if attribute['name'] == "Solved":
-                    if challenge['is_owned']:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
-                    else:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value=" ")
-                if attribute['name'] == "cssClass":
-                    if challenge['is_owned']:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
-                    else:
-                        ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
+
+        if res2['results']:
+            note_title = normalise_title(res2['results'][0]['title'])
+            challenge_title = normalise_title(challenge['name'])
+
+            if note_title == challenge_title:
+
+                for attribute in res2['results'][0]['attributes']:
+                    if attribute['name'] == "Difficulty":
+                        ea.patch_attribute(attributeId=attribute['attributeId'], value=challenge['difficulty'])
+                    if attribute['name'] == "Released":
+                        release_str = challenge['release_date']
+                        release_date = datetime.strptime(release_str, "%Y-%m-%dT%H:%M:%S.%fZ")
+                        formatted_release_date = release_date.strftime("%d %B %Y")
+                        ea.patch_attribute(attributeId=attribute['attributeId'], value=formatted_release_date)
+                    if attribute['name'] == "Solved":
+                        if challenge['is_owned']:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
+                        else:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value=" ")
+                    if attribute['name'] == "cssClass":
+                        if challenge['is_owned']:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="done")
+                        else:
+                            ea.patch_attribute(attributeId=attribute['attributeId'], value="todo")
 
         else: # doesnt already exist, create page
             release_str = challenge['release_date']
diff --git a/htb2trilium/htb2trilium_machines.py b/htb2trilium/htb2trilium_machines.py
index de8be53..3535fab 100644
--- a/htb2trilium/htb2trilium_machines.py
+++ b/htb2trilium/htb2trilium_machines.py
@@ -121,15 +121,15 @@
 	machine_count += 1
 	print('processing: ',machine_count, "/", len(machines), "("+machine['name']+")               " , end='\r')
 
+	escaped_title = machine['name'].replace("'", "\\'")
 	res = ea.search_note(
-		search="\""+machine['name']+"\"",
-		ancestorNoteId=trilium_machines_htb_folder,
-		ancestorDepth='eq1',
-		limit=1,
-		fastSearch=True,
+	    search=f"note.title = '{escaped_title}'",
+	    ancestorNoteId=trilium_machines_htb_folder,
+	    ancestorDepth='eq1',
+	    limit=1,
 	)
 
-	if res['results'] and machine['name'] == res['results'][0]['title']:
+	if res['results'] and machine['name'].strip().lower() == res['results'][0]['title'].strip().lower():
 		# page exists - lets check if the details have changed
 
 		current_html = ea.get_note_content(noteId=res['results'][0]['noteId'])