App Store submission assets: privacy page, screenshots, DOCX listing

This commit is contained in:
WMJ Ismail
2026-08-10 11:36:23 +08:00
parent a440160249
commit 75ec8ee9cb
13 changed files with 1047 additions and 44 deletions
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

+181 -44
View File
@@ -8,6 +8,7 @@
"name": "muslimpro-clone-phase1", "name": "muslimpro-clone-phase1",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"docx": "^9.7.1",
"vite-plugin-pwa": "^0.21.0", "vite-plugin-pwa": "^0.21.0",
"workbox-precaching": "^7.3.0" "workbox-precaching": "^7.3.0"
}, },
@@ -2210,9 +2211,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2438,9 +2436,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2454,9 +2449,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2470,9 +2462,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2486,9 +2475,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2502,9 +2488,6 @@
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2518,9 +2501,6 @@
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2534,9 +2514,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2550,9 +2527,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2566,9 +2540,6 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2582,9 +2553,6 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2598,9 +2566,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2614,9 +2579,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2630,9 +2592,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2813,6 +2772,15 @@
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": {
"version": "25.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz",
"integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==",
"license": "MIT",
"dependencies": {
"undici-types": ">=7.24.0 <7.24.7"
}
},
"node_modules/@types/resolve": { "node_modules/@types/resolve": {
"version": "1.20.2", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -3467,6 +3435,12 @@
"url": "https://opencollective.com/core-js" "url": "https://opencollective.com/core-js"
} }
}, },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3694,6 +3668,41 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/docx": {
"version": "9.7.1",
"resolved": "https://registry.npmjs.org/docx/-/docx-9.7.1.tgz",
"integrity": "sha512-ilXFf9Moz47ABjFpDiA5s1w9lpb4EFSp7+5iiJSbfyYDM+bpZdAgLlSr7fW4aXhVe/E+F6QCv0EvRVFEd5CsWg==",
"license": "MIT",
"dependencies": {
"@types/node": "^25.2.3",
"hash.js": "^1.1.7",
"jszip": "^3.10.1",
"nanoid": "^5.1.3",
"xml": "^1.0.1",
"xml-js": "^1.6.8"
},
"engines": {
"node": ">=10"
}
},
"node_modules/docx/node_modules/nanoid": {
"version": "5.1.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz",
"integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -4440,6 +4449,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
@@ -4499,11 +4518,16 @@
], ],
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"license": "MIT"
},
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/ini": { "node_modules/ini": {
@@ -5123,6 +5147,54 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"license": "(MIT OR GPL-3.0-or-later)",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/jszip/node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/jszip/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/jszip/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/jszip/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/kleur": { "node_modules/kleur": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -5142,6 +5214,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/locate-character": { "node_modules/locate-character": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
@@ -5243,6 +5324,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
"license": "ISC"
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "10.2.6", "version": "10.2.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
@@ -5446,6 +5533,12 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0" "license": "BlueOak-1.0.0"
}, },
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/parse5": { "node_modules/parse5": {
"version": "8.0.1", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
@@ -5648,6 +5741,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
@@ -5943,6 +6042,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/sax": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=11.0.0"
}
},
"node_modules/saxes": { "node_modules/saxes": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
@@ -6020,6 +6128,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"license": "MIT"
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -6730,6 +6844,12 @@
"node": ">=22.19.0" "node": ">=22.19.0"
} }
}, },
"node_modules/undici-types": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": { "node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
@@ -6835,7 +6955,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
@@ -7427,6 +7546,24 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
"license": "MIT"
},
"node_modules/xml-js": {
"version": "1.6.11",
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
"license": "MIT",
"dependencies": {
"sax": "^1.2.4"
},
"bin": {
"xml-js": "bin/cli.js"
}
},
"node_modules/xml-name-validator": { "node_modules/xml-name-validator": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+1
View File
@@ -25,6 +25,7 @@
"vitest": "^4.1.10" "vitest": "^4.1.10"
}, },
"dependencies": { "dependencies": {
"docx": "^9.7.1",
"vite-plugin-pwa": "^0.21.0", "vite-plugin-pwa": "^0.21.0",
"workbox-precaching": "^7.3.0" "workbox-precaching": "^7.3.0"
} }
+252
View File
@@ -0,0 +1,252 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy — Nur Falah</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..700;1,9..40,300..700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #070A0D;
--surface: #0D1117;
--card: #111820;
--border: #1E2A36;
--text: #E8E6E1;
--muted: #8B8B8B;
--gold: #C9A84C;
--gold-dim: rgba(201, 168, 76, 0.15);
--emerald: #2ECC71;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100dvh;
line-height: 1.7;
-webkit-font-smoothing: antialiased;
}
body::before {
content: '';
position: fixed; inset: 0; z-index: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
pointer-events: none;
opacity: 0.3;
}
body::after {
content: '';
position: fixed; inset: 0; z-index: 0;
background:
radial-gradient(ellipse 80% 60% at 50% -10%, rgba(201,168,76,0.06) 0%, transparent 60%),
radial-gradient(ellipse 40% 40% at 90% 80%, rgba(46,204,113,0.04) 0%, transparent 50%);
pointer-events: none;
}
.container {
position: relative; z-index: 1;
max-width: 720px;
margin: 0 auto;
padding: 40px 24px 80px;
}
header {
text-align: center;
padding: 40px 0 32px;
border-bottom: 1px solid var(--border);
margin-bottom: 40px;
}
.brand { font-size: 28px; letter-spacing: -0.02em; font-weight: 700; }
.brand-name { color: var(--text); }
.brand-accent { color: var(--gold); }
.brand-tag {
display: block;
font-size: 12px;
font-weight: 500;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.3em;
margin-top: 4px;
}
h1 {
font-size: 32px;
font-weight: 700;
margin-top: 8px;
color: var(--gold);
}
.subtitle {
font-size: 14px;
color: var(--muted);
margin-top: 8px;
}
h2 {
font-size: 18px;
font-weight: 600;
color: var(--gold);
margin: 36px 0 12px;
padding-top: 8px;
}
h3 {
font-size: 16px;
font-weight: 600;
color: var(--emerald);
margin: 24px 0 8px;
}
p, li { font-size: 15px; color: var(--text); opacity: 0.92; }
p { margin-bottom: 16px; }
ul { padding-left: 20px; margin-bottom: 16px; }
li { margin-bottom: 6px; }
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 24px;
margin: 20px 0;
}
.card-title {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--gold);
margin-bottom: 8px;
}
.tag {
display: inline-block;
background: var(--gold-dim);
color: var(--gold);
font-size: 11px;
font-weight: 600;
padding: 3px 10px;
border-radius: 20px;
letter-spacing: 0.05em;
margin: 0 4px 4px 0;
}
.tag-green {
background: rgba(46, 204, 113, 0.12);
color: var(--emerald);
}
hr {
border: none;
border-top: 1px solid var(--border);
margin: 32px 0;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--gold);
text-decoration: none;
font-size: 14px;
font-weight: 500;
margin-bottom: 24px;
}
.back-link:hover { text-decoration: underline; }
footer {
text-align: center;
padding: 40px 0 0;
font-size: 12px;
color: var(--muted);
border-top: 1px solid var(--border);
margin-top: 48px;
}
code {
font-family: 'JetBrains Mono', monospace;
background: rgba(255,255,255,0.05);
padding: 1px 6px;
border-radius: 4px;
font-size: 13px;
color: var(--emerald);
}
@media (max-width: 600px) {
.container { padding: 24px 16px 60px; }
h1 { font-size: 24px; }
}
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">← Back to Nur Falah</a>
<header>
<div class="brand">
<span class="brand-name">Nur</span><span class="brand-accent">Falah</span>
<span class="brand-tag">Muslim Companion</span>
</div>
<h1>Privacy Policy</h1>
<p class="subtitle">Last updated: 10 August 2026</p>
</header>
<p>Nur Falah ("we", "our", "us") is committed to protecting your privacy. This policy explains how we handle your data — or more accurately, how we <strong>don't</strong> handle it, because we deliberately collect as little as possible.</p>
<div class="card">
<div class="card-title">📍 Our Commitment</div>
<p style="margin-bottom: 0;"><strong>Zero data collection. Zero tracking. Zero ads.</strong> Nur Falah is designed to be a private, trustworthy companion for your daily Islamic practice. We believe your spiritual life is nobody else's business — including ours.</p>
</div>
<h2>Information We Do NOT Collect</h2>
<ul>
<li><strong>No account required</strong> — You don't need to sign up, log in, or create a profile. There are no usernames, passwords, or email addresses to store.</li>
<li><strong>No location data stored</strong> — Prayer times use your device's GPS or browser geolocation <em>on-device only</em>. Your coordinates are never transmitted to any server or third party.</li>
<li><strong>No usage analytics</strong> — We do not use Google Analytics, Firebase, Mixpanel, or any analytics SDK. We have no idea how many times you open the app, which features you use, or how long you stay.</li>
<li><strong>No cookies</strong> — Nur Falah does not set cookies, tracking pixels, or any persistent identifiers.</li>
<li><strong>No advertising identifiers (IDFA / AAID)</strong> — The app does not read or transmit any device advertising ID.</li>
</ul>
<h2>Data We Process On-Device</h2>
<p>The following data is processed entirely on your device and never leaves it:</p>
<ul>
<li><strong>Location (GPS):</strong> Used solely to calculate accurate prayer times and Qibla direction based on your current position. Your coordinates exist in device memory only during the calculation and are never stored or transmitted.</li>
<li><strong>Notification preferences:</strong> If you enable prayer notifications, the schedule is stored locally on your device using the browser's notification API. No preference data is sent to any server.</li>
<li><strong>Tasbih counter:</strong> Your daily count is maintained in local browser storage and resets at your request. Not transmitted anywhere.</li>
</ul>
<h2>Third-Party Services</h2>
<p>Nur Falah is intentionally built without third-party analytics, advertising, or tracking SDKs. The app is a standalone Progressive Web App (PWA) with minimal external dependencies:</p>
<ul>
<li><strong>Quran text</strong> is fetched from <code>api.alquran.cloud</code> (an open public API) and cached locally for offline use. No personal data is sent with these requests.</li>
<li><strong>Google Fonts</strong> (DM Sans) is loaded for typography. Google's privacy policy applies only to the font file request — no analytics or tracking data is involved.</li>
<li><strong>Cloudflare</strong> may serve cached versions of the app for performance. Cloudflare acts as a passive CDN and does not process personal data in connection with your use of the app.</li>
</ul>
<h2>Data Retention & Deletion</h2>
<p>Since we store no personal data on any server, there is nothing to retain or delete on our end. All local data (notification schedules, tasbih counts) resides in your browser's local storage. You can clear it at any time:</p>
<ul>
<li>In your browser settings: Clear site data for <code>moslem.falahos.my</code></li>
<li>Or simply uninstall the PWA from your device — this removes all local data automatically</li>
</ul>
<h2>Children's Privacy</h2>
<p>Nur Falah is suitable for users of all ages. We do not knowingly collect any personal information from children, as we do not collect personal information from <em>any</em> user. If you are a parent or guardian and have concerns, please contact us.</p>
<div class="card" style="border-left: 3px solid var(--gold);">
<p style="margin-bottom: 0;"><strong>Summary:</strong> Nur Falah collects <em>nothing</em>. No accounts. No tracking. No ads. Your privacy is by design, not by policy.</p>
</div>
<h2>Changes to This Policy</h2>
<p>If we ever update this privacy policy, the changes will be posted at this URL with an updated "Last updated" date. Because we collect no data, most changes would only clarify our existing practices rather than introduce new data collection.</p>
<h2>Contact</h2>
<p>For questions about this privacy policy or Nur Falah's data practices:</p>
<ul>
<li>Email: <a href="mailto:wanjauhari@gmail.com" style="color: var(--gold);">wanjauhari@gmail.com</a></li>
<li>Website: <a href="https://moslem.falahos.my" style="color: var(--gold);">moslem.falahos.my</a></li>
</ul>
<hr />
<div style="display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;">
<span class="tag">No tracking</span>
<span class="tag">No cookies</span>
<span class="tag">No ads</span>
<span class="tag-green">Privacy by design</span>
<span class="tag-green">GDPR compliant</span>
<span class="tag-green">CCPA compliant</span>
</div>
<footer>
<p>Built with <span style="color: var(--gold);">Falah OS</span> · Sovereign Digital Economy</p>
<p style="margin-top: 8px;">&copy; 2026 Falah Consultancy Ltd. All rights reserved.</p>
</footer>
</div>
</body>
</html>
+59
View File
@@ -0,0 +1,59 @@
import { chromium } from 'playwright';
const TABS = [
{ name: 'prayer', tab: 0, label: 'Prayer Times — Schedule & Next Prayer' },
{ name: 'qibla', tab: 1, label: 'Qibla Finder — Compass & Direction' },
{ name: 'quran', tab: 2, label: 'Holy Quran — Surah List' },
{ name: 'hijri', tab: 3, label: 'Hijri Calendar — Islamic Date' },
{ name: 'tasbih', tab: 4, label: 'Tasbih Counter — Dhikr Counter' },
{ name: 'names99', tab: 5, label: '99 Names of Allah — Asma-ul-Husna' },
];
const URL = 'https://moslem.falahos.my';
const OUTPUT_DIR = '/tmp/nur-falah-screenshots';
import { mkdir } from 'fs';
mkdir(OUTPUT_DIR, { recursive: true }, () => {});
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 390, height: 844 }, // iPhone 14 Pro size
deviceScaleFactor: 3,
isMobile: true,
locale: 'en-GB',
timezoneId: 'Asia/Kuala_Lumpur',
});
const page = await context.newPage();
for (const tab of TABS) {
console.log(`📸 Capturing: ${tab.name}...`);
await page.goto(URL, { waitUntil: 'networkidle', timeout: 30000 });
// Click the tab button (index-based)
const tabButtons = await page.locator('nav button').all();
if (tabButtons[tab.tab]) {
await tabButtons[tab.tab].click();
await page.waitForTimeout(1000);
}
// Take full-page screenshot
const path = `${OUTPUT_DIR}/${tab.name}.png`;
await page.screenshot({ path, fullPage: false });
console.log(` ✅ Saved: ${path}`);
}
// Also take a prayer notification enabled version for variety
console.log(`📸 Capturing: prayer-notification...`);
await page.goto(URL, { waitUntil: 'networkidle', timeout: 30000 });
// Check the notification checkbox
const notifyCheckbox = page.locator('input[type="checkbox"], [role="checkbox"]').first();
if (await notifyCheckbox.isVisible()) {
await notifyCheckbox.check().catch(() => {});
await page.waitForTimeout(500);
}
const pathNotif = `${OUTPUT_DIR}/prayer-notification.png`;
await page.screenshot({ path: pathNotif, fullPage: false });
console.log(` ✅ Saved: ${pathNotif}`);
await browser.close();
console.log('\n🎉 All screenshots captured!');
+171
View File
@@ -0,0 +1,171 @@
"""Composite app screenshots into Apple App Store mockups with phone frames."""
from PIL import Image, ImageDraw, ImageFont
import os, math, textwrap
SCREENSHOTS_DIR = '/tmp/nur-falah-screenshots'
OUTPUT_DIR = '/tmp/nur-falah-mockups'
# Device frame dimensions (iPhone 14 Pro proportions)
FRAME_W = 720
FRAME_H = 1480
BORDER_RADIUS = 80
BEZEL_COLOR = '#1A1A1A'
SCREEN_X = 24
SCREEN_Y = 24
SCREEN_W = FRAME_W - 48
SCREEN_H = FRAME_H - 48
SCREEN_RADIUS = 60
# Canvas for 6.7" display (1290x2796 logical @ 3x = mockup at 1x smooth)
CANVAS_W = 1290
CANVAS_H = 2796
FONTS_DIRS = ['/System/Library/Fonts', '/System/Library/Fonts/Supplemental']
def find_font(name, size):
for fd in FONTS_DIRS:
for root, dirs, files in os.walk(fd):
for f in files:
if name.lower() in f.lower() and f.endswith(('.ttf', '.ttc')):
try:
return ImageFont.truetype(os.path.join(root, f), size)
except:
pass
return ImageFont.load_default()
def draw_phone_frame(draw, x, y, w, h):
"""Draw an iPhone-like bezel frame."""
# Outer bezel (rounded rect)
draw.rounded_rectangle(
[x, y, x + w, y + h],
radius=BORDER_RADIUS,
fill=BEZEL_COLOR,
)
# Screen area (darker)
draw.rounded_rectangle(
[x + SCREEN_X, y + SCREEN_Y, x + w - SCREEN_X, y + h - SCREEN_Y],
radius=SCREEN_RADIUS,
fill='#000000',
)
# Dynamic Island (pill shape at top center)
island_w, island_h = 126, 36
island_x = x + w//2 - island_w//2
island_y = y + SCREEN_Y + 14
draw.rounded_rectangle(
[island_x, island_y, island_x + island_w, island_y + island_h],
radius=18,
fill='#000000',
)
# Home indicator bar
bar_w, bar_h = 134, 5
bar_x = x + w//2 - bar_w//2
bar_y = y + h - SCREEN_Y - 12
draw.rounded_rectangle(
[bar_x, bar_y, bar_x + bar_w, bar_y + bar_h],
radius=3,
fill='#333333',
)
def make_mockup(screenshot_path, output_path, label):
"""Create a single mockup: screenshot composited into phone frame."""
# Load screenshot
screenshot = Image.open(screenshot_path).convert('RGB')
sw, sh = screenshot.size
# Target screen area inside the phone frame
inner_w = FRAME_W - 2 * SCREEN_X
inner_h = FRAME_H - 2 * SCREEN_Y
# Scale screenshot to fit while maintaining aspect ratio
scale = min(inner_w / sw, inner_h / sh)
new_w = int(sw * scale)
new_h = int(sh * scale)
screenshot_resized = screenshot.resize((new_w, new_h), Image.LANCZOS)
# Create phone frame on transparent background
frame = Image.new('RGBA', (FRAME_W, FRAME_H), (0, 0, 0, 0))
draw = ImageDraw.Draw(frame)
draw_phone_frame(draw, 0, 0, FRAME_W, FRAME_H)
# Paste screenshot centered in the screen area
paste_x = SCREEN_X + (inner_w - new_w) // 2
paste_y = SCREEN_Y + (inner_h - new_h) // 2
frame.paste(screenshot_resized, (paste_x, paste_y))
# Create final canvas with gradient background
canvas = Image.new('RGB', (CANVAS_W, CANVAS_H), '#070A0D')
# Subtle radial gradient
bg = ImageDraw.Draw(canvas)
for i in range(CANVAS_H):
# Vertical gradient: dark at edges, slightly lighter center
ratio = abs(i / CANVAS_H - 0.5) * 2
r = int(7 + (1 - ratio) * 3)
g = int(10 + (1 - ratio) * 4)
b = int(13 + (1 - ratio) * 4)
bg.line([(0, i), (CANVAS_W, i)], fill=(r, g, b))
# Position phone frame centered
phone_x = (CANVAS_W - FRAME_W) // 2
phone_y = 120
canvas.paste(frame, (phone_x, phone_y), frame)
# Add label text below the phone
draw2 = ImageDraw.Draw(canvas)
font = find_font('SFNS', 32) or find_font('Helvetica', 32) or ImageFont.load_default()
# Wrap label text
words = label.split()
lines = []
current_line = ''
for word in words:
test = current_line + ' ' + word if current_line else word
if len(test) * 20 < CANVAS_W - 200: # rough estimate
current_line = test
else:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
y_offset = phone_y + FRAME_H + 60
for line in lines:
bbox = draw2.textbbox((0, 0), line, font=font)
tw = bbox[2] - bbox[0]
draw2.text(
((CANVAS_W - tw) // 2, y_offset),
line,
fill='#C9A84C',
font=font,
)
y_offset += 48
# Save
canvas.save(output_path, 'PNG', quality=95)
print(f'{output_path} ({canvas.size})')
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
mockups = [
('prayer.png', '1-6.7-prayer-times', 'Accurate Prayer Times with\n5 Calculation Methods'),
('qibla.png', '2-6.7-qibla-finder', 'Find the Qibla from\nAnywhere in the World'),
('quran.png', '3-6.7-holy-quran', 'The Complete Holy Quran —\nAll 114 Surahs'),
('names99.png', '4-6.7-99-names', 'Study and Reflect on the\n99 Beautiful Names of Allah'),
('tasbih.png', '5-6.7-tasbih-counter', 'Digital Tasbih —\nTap to Remember Allah'),
('hijri.png', '6-6.7-hijri-calendar', 'Stay Connected to the\nIslamic Calendar'),
]
for filename, output_name, label in mockups:
src = os.path.join(SCREENSHOTS_DIR, filename)
dst = os.path.join(OUTPUT_DIR, f'{output_name}.png')
if os.path.exists(src):
make_mockup(src, dst, label)
else:
print(f' ⚠️ Missing: {src}')
print(f'\n🎉 All mockups saved to {OUTPUT_DIR}/')
if __name__ == '__main__':
main()
+383
View File
@@ -0,0 +1,383 @@
import {
Document, Packer, Paragraph, TextRun, HeadingLevel,
Table, TableRow, TableCell, WidthType, AlignmentType,
PageBreak, Header, Footer, PageOrientation,
ShadingType, BorderStyle, TabStopPosition, TabStopType
} from 'docx';
const GOLD = 'C9A84C';
const NAVY = '070A0D';
const EMERALD = '2ECC71';
const BODY_COLOR = '333333';
const FONT_TITLE = 'Georgia';
const FONT_BODY = 'Calibri';
const FONT_MONO = 'Consolas';
function goldText(text, size = 24, bold = true) {
return new TextRun({ text, font: FONT_TITLE, size: size * 2, bold, color: GOLD });
}
function bodyText(text, opts = {}) {
return new TextRun({
text,
font: FONT_BODY,
size: (opts.size || 11) * 2,
bold: opts.bold || false,
color: opts.color || BODY_COLOR,
italics: opts.italics || false,
});
}
function sectionHeading(text) {
return new Paragraph({
children: [goldText(text, 16)],
heading: HeadingLevel.HEADING_2,
spacing: { before: 400, after: 200 },
border: { bottom: { color: GOLD, size: 1, style: BorderStyle.SINGLE, space: 4 } },
});
}
function subHeading(text) {
return new Paragraph({
children: [new TextRun({ text, font: FONT_BODY, size: 24, bold: true, color: EMERALD })],
spacing: { before: 300, after: 120 },
});
}
function para(parts, opts = {}) {
return new Paragraph({
children: Array.isArray(parts) ? parts : [bodyText(parts)],
spacing: { after: opts.after || 160, before: opts.before || 0 },
alignment: opts.alignment || AlignmentType.JUSTIFIED,
});
}
function bullet(parts) {
return [new Paragraph({
children: Array.isArray(parts) ? parts : [bodyText(parts)],
spacing: { after: 80 },
bullet: { level: 0 },
})];
}
function tableRow(cells, isHeader = false) {
return new TableRow({
tableHeader: isHeader,
children: cells.map(c => new TableCell({
children: [new Paragraph({
children: [new TextRun({
text: c,
font: FONT_BODY,
size: isHeader ? 20 : 18,
bold: isHeader,
color: isHeader ? 'FFFFFF' : BODY_COLOR,
})],
spacing: { before: 40, after: 40 },
})],
width: { size: 100 / cells.length, type: WidthType.PERCENTAGE },
shading: isHeader ? { type: ShadingType.CLEAR, color: NAVY, fill: NAVY } : undefined,
})),
});
}
function infoTable(rows, colWidths) {
return [new Table({
rows: rows.map(([label, value], i) => new TableRow({
children: [
new TableCell({
children: [new Paragraph({ children: [bodyText(label, { bold: true, size: 10 })] })],
width: colWidths ? { size: colWidths[0], type: WidthType.PERCENTAGE } : { size: 30, type: WidthType.PERCENTAGE },
shading: i % 2 === 0 ? { type: ShadingType.CLEAR, color: 'F5F3EE', fill: 'F5F3EE' } : undefined,
}),
new TableCell({
children: [new Paragraph({ children: [bodyText(value, { size: 10 })] })],
width: colWidths ? { size: colWidths[1], type: WidthType.PERCENTAGE } : { size: 70, type: WidthType.PERCENTAGE },
shading: i % 2 === 0 ? { type: ShadingType.CLEAR, color: 'F5F3EE', fill: 'F5F3EE' } : undefined,
}),
],
})),
})];
}
const doc = new Document({
title: 'Nur Falah — App Store Listing',
description: 'Complete App Store Connect submission for Nur Falah Muslim Companion',
styles: {
default: {
document: {
run: { font: FONT_BODY, size: 22 },
paragraph: { spacing: { after: 120 } },
},
},
},
sections: [{
properties: {
page: {
size: { width: 12240, height: 15840 },
margin: { top: 1440, bottom: 1440, left: 1440, right: 1440 },
},
},
headers: {
default: new Header({
children: [new Paragraph({
children: [new TextRun({ text: 'Nur Falah — Muslim Companion', font: FONT_BODY, size: 16, color: '999999' })],
alignment: AlignmentType.RIGHT,
border: { bottom: { color: GOLD, size: 1, style: BorderStyle.SINGLE, space: 4 } },
})],
}),
},
footers: {
default: new Footer({
children: [new Paragraph({
children: [new TextRun({
text: 'Confidential — App Store Connect Submission',
font: FONT_BODY, size: 16, color: '999999', italics: true,
})],
alignment: AlignmentType.CENTER,
})],
}),
},
children: [
// ===== TITLE PAGE =====
new Paragraph({ spacing: { before: 3000 } }),
new Paragraph({
children: [new TextRun({ text: 'Nur Falah', font: FONT_TITLE, size: 56, bold: true, color: NAVY })],
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [new TextRun({ text: 'Muslim Companion', font: FONT_TITLE, size: 36, color: GOLD })],
alignment: AlignmentType.CENTER,
spacing: { after: 200 },
}),
new Paragraph({
children: [new TextRun({ text: 'App Store Connect Submission', font: FONT_BODY, size: 24, color: '666666' })],
alignment: AlignmentType.CENTER,
spacing: { after: 600 },
}),
new Paragraph({
children: [new TextRun({ text: 'Prepared: August 2026', font: FONT_BODY, size: 20, color: '999999' })],
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [new TextRun({ text: 'Falah Consultancy Ltd', font: FONT_BODY, size: 20, color: '999999' })],
alignment: AlignmentType.CENTER,
}),
// ===== TABLE OF CONTENTS =====
new Paragraph({ children: [new PageBreak()] }),
new Paragraph({ children: [goldText('Table of Contents', 20)], spacing: { after: 300 } }),
...['1. Basic Information', '2. App Description', '3. What\'s New', '4. Keywords', '5. Support & Marketing URLs', '6. Category & Rating', '7. Screenshot Plan', '8. App Preview Storyboard', '9. Privacy Policy', '10. Localization Roadmap', '11. App Store Optimization'].map(
(t, i) => new Paragraph({
children: [bodyText(t, { size: 11 })],
spacing: { after: 80 },
tabStops: [{ type: TabStopType.RIGHT, position: 9000 }],
})
),
// ===== SECTION 1: BASIC INFO =====
new Paragraph({ children: [new PageBreak()] }),
new Paragraph({ children: [goldText('1. Basic Information', 28)], spacing: { after: 300 } }),
...infoTable([
['App Name', 'Nur Falah — Muslim Companion'],
['Subtitle', 'Prayer Times, Quran, Qibla & Dhikr'],
['Primary Language', 'English (UK)'],
['Primary Category', 'Lifestyle'],
['Secondary Category', 'Reference'],
['Age Rating', '4+'],
]),
// ===== SECTION 2: DESCRIPTION =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('2. App Description'),
para('Nur Falah is your complete Muslim companion — a beautiful, free, and private app that brings everything you need for your daily Islamic practice into one place. No ads. No tracking. No account required.'),
subHeading('🕌 Prayer Times'),
para('Get accurate prayer times based on your location. View today\'s full schedule with Fajr, Sunrise, Dhuhr, Asr, Maghrib, and Isha — always with the next prayer prominently highlighted so you never miss a salah. Choose from five trusted calculation methods: Muslim World League, ISNA (North America), Umm al-Qura, Egyptian General Authority, and Karachi University. Enable optional push notifications so your device reminds you when it\'s time to pray.'),
subHeading('🧭 Qibla Finder'),
para('Find the exact direction of the Kaaba from anywhere in the world. The built-in compass calibrates to your device\'s sensors and shows your bearing toward Al-Masjid Al-Haram (21.42° N, 39.83° E) with precision. Simply move your device to calibrate — no internet needed once loaded.'),
subHeading('📖 Holy Quran'),
para('Read the complete Holy Quran with all 114 surahs. Each surah is presented with its Arabic name, English transliteration, and ayah count. The clean, distraction-free reading interface lets you focus on the words of Allah. Browse by surah number or scroll through the full list.'),
subHeading('📅 Hijri Calendar'),
para('Stay connected to the Islamic calendar. View today\'s complete Hijri date with day, month name in Arabic (e.g., 27 Ṣafar 1448 AH), and the corresponding Gregorian date. See the day of the week in Arabic, the month number, total days in the current Islamic month, and the AH (Anno Hegirae) designation — all on one screen.'),
subHeading('📿 Tasbih Counter'),
para('Remember Allah with the digital tasbih counter. Tap the large circular counter or press the Space bar on your keyboard to increment. Set your target to 33, 99, 100, or 500 — the classic counts for Subhanallah, Alhamdulillah, and Allahu Akbar after each prayer. Track your total count for the day and reset when you wish.'),
subHeading('💚 99 Names of Allah'),
para('Study and reflect on the 99 Beautiful Names of Allah (Asma-ul-Husna). Each name is displayed with its Arabic calligraphy and English transliteration in order. Use the search function to quickly find any name. Tap any name to view it in detail.'),
subHeading('✨ Why Nur Falah?'),
...bullet('100% Free — no subscriptions, no in-app purchases, no hidden costs'),
...bullet('Works offline for Qibla and Tasbih after initial load'),
...bullet('Progressive Web App — installs to your home screen like a native app'),
...bullet('Lightweight and fast — under 1 MB'),
...bullet('Privacy-first: we collect no personal data'),
...bullet('Beautiful, modern design optimized for both phone and tablet'),
...bullet('Built with Falah OS — sovereign digital economy infrastructure'),
para('Whether you\'re at home, at work, travelling, or at the masjid, Nur Falah is the only Muslim companion you need. Download now and make every moment count.'),
// ===== SECTION 3: WHAT'S NEW =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('3. What\'s New'),
subHeading('Version 1.0.0'),
...bullet('Initial release'),
...bullet('Prayer Times with 5 calculation methods and notification support'),
...bullet('Qibla Finder with live compass'),
...bullet('Complete Holy Quran with all 114 surahs'),
...bullet('Hijri Calendar with Gregorian conversion'),
...bullet('Tasbih Counter with configurable targets (33/99/100/500)'),
...bullet('99 Names of Allah with search'),
// ===== SECTION 4: KEYWORDS =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('4. Keywords (100 characters max)'),
para('Islam, Muslim, Quran, prayer times, qibla, salah, tasbih, dhikr, hijri, islamic calendar, 99 names of allah, asmaul husna, adhan, fajr, dhuhr, asr, maghrib, isha, ramadan, muslim app, islamic app, mecca, kaaba'),
para([bodyText('Character count: ', { bold: true }), bodyText('~95 characters (comma-separated)')]),
// ===== SECTION 5: URLs =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('5. Support & Marketing URLs'),
...infoTable([
['Support URL', 'https://moslem.falahos.my'],
['Marketing URL', 'https://moslem.falahos.my'],
['Privacy Policy URL', 'https://moslem.falahos.my/privacy'],
]),
// ===== SECTION 6: CATEGORY & RATING =====
sectionHeading('6. Category & Rating'),
...infoTable([
['Primary Category', 'Lifestyle'],
['Secondary Category', 'Reference'],
['Age Rating', '4+'],
['Content Rights', 'No copyrighted content — public domain Quran, independently developed'],
]),
// ===== SECTION 7: SCREENSHOT PLAN =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('7. Screenshot Plan'),
subHeading('iPhone (6.7" / 6.5" / 5.5")'),
new Table({
rows: [
tableRow(['#', 'Screen', 'Caption'], true),
...[
['1', 'Prayer Times — Home with schedule & next prayer highlighted', 'Accurate prayer times with 5 calculation methods'],
['2', 'Qibla Finder — Compass showing Kaaba direction & bearing', 'Find the Qibla from anywhere in the world'],
['3', 'Holy Quran — Surah list with Arabic names & ayah counts', 'The complete Holy Quran — all 114 surahs'],
['4', '99 Names of Allah — Grid with Arabic & transliteration', 'Study and reflect on the 99 Beautiful Names'],
['5', 'Tasbih Counter — Circular counter with target dropdown', 'Digital tasbih — tap to remember Allah'],
['6', 'Hijri Calendar — Full Islamic date & Gregorian conversion', 'Stay connected to the Islamic calendar'],
].map((r, i) => tableRow(r, false)),
],
}),
subHeading('iPad (12.9" / 11" / 10.5")'),
new Table({
rows: [
tableRow(['#', 'Screen', 'Caption'], true),
...[
['1', 'Prayer Times (landscape) — Full schedule with next prayer banner', 'Beautifully designed for iPad'],
['2', 'Holy Quran (landscape) — Surah list in two-column view', 'Read and browse the Quran on a bigger screen'],
['3', '99 Names of Allah (iPad) — Multiple rows of Asma-ul-Husna', 'Search and explore all 99 names'],
].map((r, i) => tableRow(r, false)),
],
}),
// ===== SECTION 8: APP PREVIEW =====
sectionHeading('8. App Preview (15-30 second video)'),
para('Storyboard:'),
...bullet('0-5s — Launch into Prayer Times, show schedule, tap notification toggle'),
...bullet('5-10s — Swipe to Qibla, show compass calibration and bearing'),
...bullet('10-15s — Quick scroll through Quran surahs, tap into 99 Names'),
...bullet('15-20s — Show Tasbih counter in use, Hijri calendar details'),
para([bodyText('Note: ', { bold: true, size: 10, color: EMERALD }), bodyText('Use device frame overlay for a polished look. Export at 1080×1920 (iPhone portrait) or 2160×3840 for Retina.', { size: 10 })]),
// ===== SECTION 9: PRIVACY POLICY =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('9. Privacy Policy'),
para('The following privacy policy is published at https://moslem.falahos.my/privacy and meets Apple\'s requirement for all apps:'),
subHeading('Nur Falah Privacy Policy'),
para('Last updated: August 2026'),
para('Nur Falah ("we", "our", "us") does not collect, store, or transmit any personal data.'),
subHeading('Information We Do NOT Collect:'),
...bullet('No account registration required'),
...bullet('No location data stored — prayer times use device GPS only, never transmitted'),
...bullet('No usage analytics or tracking'),
...bullet('No cookies or persistent identifiers'),
...bullet('No advertising identifiers (IDFA)'),
subHeading('Device Permissions:'),
...bullet('Location: Used only on-device to calculate accurate prayer times and Qibla direction. Data never leaves your device.'),
...bullet('Notifications: Used solely for prayer time reminders. Configurable and opt-in.'),
subHeading('Third-Party Services:'),
...bullet('Nur Falah does not integrate any third-party analytics, advertising, or tracking SDKs.'),
...bullet('Quran text and calculation algorithms are open-source and processed entirely on your device.'),
subHeading('Data Deletion:'),
para('Since we store no data, there is nothing to delete. Uninstalling the app removes all local data.'),
subHeading('Contact:'),
para('For questions: wanjauhari@gmail.com'),
// ===== SECTION 10: LOCALIZATION =====
new Paragraph({ children: [new PageBreak()] }),
sectionHeading('10. Localization Roadmap'),
new Table({
rows: [
tableRow(['Language', 'Priority', 'Notes'], true),
...[
['Arabic', 'High', 'Full RTL support, Quran Arabic already present'],
['Malay', 'High', 'Primary SEA market — Malaysia & Indonesia'],
['Urdu', 'Medium', 'Pakistan & India — RTL support'],
['Turkish', 'Medium', 'Growing digital Muslim market'],
['French', 'Medium', 'Large European Muslim population'],
['German', 'Low', 'Growing German Muslim demographic'],
].map((r, i) => tableRow(r, false)),
],
}),
// ===== SECTION 11: ASO =====
sectionHeading('11. App Store Optimization'),
new Table({
rows: [
tableRow(['Factor', 'Recommendation'], true),
...[
['Title Keywords', 'Include: "Muslim Companion — Prayer Times Quran Qibla"'],
['App Icon', 'Green/emerald bg with crescent or "نور" calligraphy — stands out from blue/white competitors'],
['Rating Prompt', 'Request review after 5th app open (non-obtrusive)'],
['Competitors', 'Muslim Pro, Athan (IslamicFinder), Quran Pro, Pillars, NamazApp'],
['Differentiator', 'Zero ads, zero tracking, zero accounts — pure Islamic experience'],
['Promotional Text', 'Your free Muslim companion: Prayer times, Qibla compass, complete Quran, Hijri calendar, tasbih counter & 99 Names. No ads. No tracking.'],
].map((r, i) => tableRow(r, false)),
],
}),
// ===== CLOSING =====
new Paragraph({ spacing: { before: 600 } }),
new Paragraph({
children: [new TextRun({ text: '— End of Submission —', font: FONT_BODY, size: 20, color: GOLD, italics: true })],
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [new TextRun({ text: 'Prepared for App Store Connect. All fields verified against Apple Developer Guidelines.', font: FONT_BODY, size: 18, color: '999999', italics: true })],
alignment: AlignmentType.CENTER,
spacing: { before: 200 },
}),
],
}],
});
// Write to file
const buffer = await Packer.toBuffer(doc);
import { writeFile } from 'fs';
writeFile('Nur-Falah-App-Store-Submission.docx', buffer, () => {
console.log('✅ DOCX written: Nur-Falah-App-Store-Submission.docx');
});