+ i started as a creative working across media & advertising industry after my professional
+ communication studies at rmit university vietnam.
+
as an editor and copywriter, my work revolved around generating ideas and producing multimedia
+ artifacts across digital platforms.
+
then i further explored the changing landscape driven by platforms by studying
+ digital communication at qut, queensland, australia.
+ here i expand my work with research, data analysis & visualisation.
+
currently i'm an data analyst working for a digital agency focusing on government digital
+ services. based in brisbane, australia.
+
i also explore & do other things - illustration, collage, and photography.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ everything is a matter of information exchange.
+ from data, message, and idea comes information, knowledge, and action.
+ a stint in advertising taught me everything we read, listen, and watch is persuasion.
+ this doesn't necessarily involve understanding and accountability.
+
what i strive for is resonating, thoughtful, and hopefully critical work.
+ that is, better communication through research, data analysis and information design.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..b0bda95
--- /dev/null
+++ b/main.js
@@ -0,0 +1,179 @@
+/* header */
+// grab header and desktop header
+// insert the contents of header into the desktop one
+// using the library https://github.com/camwiegert/in-view
+
+const header = document.querySelector(".header");
+const desktopHeader = document.querySelector(".header-desktop");
+desktopHeader.innerHTML = header.innerHTML;
+
+// 1. when the header enters the viewport hide the desktop header (by removing class)
+// 2. when the header leaves, show it (by adding visible class)
+// one line function - no need curly bracket
+inView(".header")
+ .on("enter", (el) => desktopHeader.classList.remove("visible"))
+ .on("exit", (el) => desktopHeader.classList.add("visible"));
+
+/* parallax tilt image */
+VanillaTilt.init(document.querySelectorAll(".image"), {
+ max: 25,
+ speed: 400,
+ easing: "cubic-bezier(.03,.98,.52,.99)",
+ glare: true,
+});
+
+/* fading images in on scroll */
+// get all images to fade in
+// add the visible class which toggles the opacity
+inView(".fade")
+ .on("enter", (img) => img.classList.add("visible"))
+ .on("exit", (img) => img.classList.remove("visible"));
+
+/* fields */
+// click the .register-button, run a function
+// grab the .front element and add a class of .slide-up
+// const registerButton = document.querySelector(".register-button");
+// registerButton.addEventListener("click", (event) => {
+// // stop any default actions from happening
+// event.preventDefault();
+// const frontEl = document.querySelector(".front");
+// frontEl.classList.add("slide-up");
+// });
+
+/* stripe */
+// create a stripe client
+const stripe = Stripe("pk_test_cucWEL0zZ0Ttl8sDgYcAdeD6");
+
+// create an instance of elements
+var elements = stripe.elements();
+
+// custom styling can be passed to options when creating an element
+// note that this demo uses a wider style of styles than the guide below
+const style = {
+ base: {
+ color: "#32325d",
+ lineHeight: "18px",
+ fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
+ fontSmoothing: "antialiased",
+ fontSize: "16px",
+ "::placeholder": {
+ color: "#895a4a",
+ },
+ },
+ invalid: {
+ color: "#895a4a",
+ iconColor: "#895a4a",
+ },
+};
+
+// create an instance of the card element
+const card = elements.create("card", { style: style });
+
+// add an instance of the card element into 'card-element'
+card.mount("#card-element");
+
+// handle real-time validation errors from the card element
+card.addEventListener("change", function (event) {
+ if (event.error) {
+ displayError.textContent = event.error.message;
+ } else {
+ displayError.textContent = "";
+ }
+});
+
+// handle form submission
+const form = document.getElementById("payment-form");
+const errorElement = document.getElementById("card-errors");
+
+form.addEventListener("submit", function (event) {
+ event.preventDefault();
+
+ //here we lock the form
+ form.classList.add("processing");
+ stripe.createToken(card).then(function (result) {
+ if (result.error) {
+ // here we unlock the form again if there's an error
+ form.classList.remove("processing");
+ // set the error text to the the error message
+ errorElement.textContent = result.error.message;
+ } else {
+ // now we take over to handle sending the token to our server
+ stripeTokenHandler(result.token);
+ }
+ });
+});
+
+function stripeTokenHandler(token) {
+ // here we handle and make our actual payment
+ // 1. make a variable for our token, name and email
+ // const stripe_token = token.id
+ const nameEl = document.querySelector("#name");
+ const emailEl = document.querySelector("#email");
+ // 2. we are going to grab form action url from the form
+ const backendUrl = form.getAttribute("action");
+ // 3. send the data off to the url using fetch
+ // we use fetch to POST to our url vs GET
+ fetch(backendUrl, {
+ method: "POST",
+ // tell it we send across json data
+ headers: {
+ "Content-Type": "application/json",
+ },
+ // here we send the data across
+ // 4. need to make sure the data is ready/secure to be sent over
+ body: JSON.stringify({
+ order: {
+ stripe_token: token.id,
+ // grab the value from the name and email element
+ name: nameEl.value,
+ email: emailEl.value,
+ },
+ }),
+ })
+ // with fetch we get back our response which we turn into json
+ .then((response) => response.json())
+ // then we get it back as data to do stuff with
+ .then((data) => {
+ // here we check we actually get an order back then do something with it
+ if (data.order) {
+ const order = data.order;
+ // ${order.name}
+ // $(order.email)
+ // tell the users the payment is successful
+ form.querySelector(".form-title").textContent = "Payment successful!";
+ form.querySelector(
+ ".form-fields"
+ ).textContent = `Thank you ${order.name},
+ your payment was successful and we have sent an email to ${order.email} `;
+ form.classList.remove("processing");
+ }
+ })
+ // if there is an error, we can do something as well
+ .catch((error) => {
+ form.classList.remove("processing");
+ // tell users there was an error
+ errorElement.textContent = `There was an error with payment
+ , please try again or contact us at`;
+ });
+}
+
+/* smoothing scrolling */
+const anchors = document.querySelectorAll("a");
+
+// loop over them
+anchors.forEach((anchor) => {
+ // listen for clicks on each one
+ anchor.addEventListener("click", (event) => {
+ // grab the hred attribute
+ const href = anchor.getAttribute("href");
+ // if the href starts with a #
+ if (href.charAt(0) === "#") {
+ // stop the default action
+ event.preventDefault();
+ // find the element the href points to and scroll it into view
+ document.querySelector(href).scrollIntoView({
+ behavior: "smooth",
+ });
+ }
+ });
+});
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..73fcb1e
--- /dev/null
+++ b/style.css
@@ -0,0 +1,197 @@
+:root {
+ --background-color: #333e40;
+ --color-2: #895a4a;
+ --color-3: #733B1A;
+ --text-color: #F2D6B3;
+ --text-color-2: #D9BB96;
+ --text-color-3: #e5d7cd;
+
+ --breakpoint-1: 840px;
+ --boundeins-strange-font: 'Bandeins Strange', sans-serif;
+}
+
+
+body {
+ font-family: var(--boundeins-strange-font);
+ font-size: 16px;
+ line-height: 1.5;
+
+ color: var(--text-color);
+
+ background-color: var(--background-color);
+ background-image: url(assets/images/background_web_updated_april23.png);
+ background-repeat: no-repeat;
+ background-size: 1080px auto;
+ /* width and height */
+ /* background-size: 100% auto; */
+ /* background-size: 100vw auto; */
+ /* center X axis and top edge */
+ background-position: center top;
+}
+
+a {
+ text-decoration: none;
+ color: var(--text-color);
+}
+
+
+ul {
+ list-style-type: none;
+}
+
+.heading {
+ color: var(--text-color);
+ /* -webkit-text-fill-color: transparent; */
+ /* -webkit-text-stroke-width: 3px; */
+ /* -webkit-text-stroke-color: var(--text-color-2); */
+ font-size: 2.5em;
+ line-height: 1;
+}
+
+img {
+ filter: saturate(0.7) sepia(0.2) contrast(1) brightness(1);
+}
+
+.container {
+ max-width: 1080px;
+ margin: 0 auto;
+ padding-left: 2rem;
+ padding-right: 2rem;
+}
+
+.heading-orange {
+ font-size: 48px;
+ /* -webkit-text-stroke-color: var(--color-2); */
+ margin-bottom: 3rem;
+}
+
+blockquote {
+ margin-left: 0;
+ color: var(--text-color-2);
+ font-size: 24px;
+}
+
+.cite {
+ display: inline-block;
+ color: var(--color-2);
+ line-height: 1;
+ padding: 24px 18px;
+ border-radius: 16px;
+ border: solid 3px var(--color-2);
+ /* text-transform: uppercase; */
+}
+
+/* make it property display of block to take up full width */
+.speaker-image {
+ display: block;
+ margin-bottom: 12px;
+}
+
+.speaker-name,
+.speaker-title {
+ margin: 0;
+ line-height: 1.33;
+}
+
+.speaker-title {
+ color: var(--text-color-2);
+}
+
+.schedule-container {
+ border: solid 3px var(--color-2);
+ border-radius: 16px;
+
+ /* this gives us natural scrolling beahviour on touch screen */
+ -webkit-overflow-scrolling: touch;
+}
+
+.schedule-container::-webkit-scrollbar {
+ width: 12px;
+ height: 14px;
+ background-color: transparent;
+}
+
+.schedule-container::-webkit-scrollbar-thumb {
+ background-color: var(--color-2);
+ /* border: solid 2px #fff; */
+ border-radius: 8px;
+}
+
+.schedule {
+ padding: 48px 24px;
+}
+
+.schedule-time {
+ color: var(--text-color-2);
+ font-size: 24px;
+ line-height: 1;
+ margin-bottom: 8px;
+}
+
+.schedule-day {
+ /* languages likes japanese sometimes have a vertical writing style */
+ writing-mode: vertical-rl;
+ /* we can change text orientation to sideways */
+ text-orientation: sideways;
+ /* then we rotate the text */
+ transform: rotate(180deg);
+ padding: 24px;
+ line-height: 1;
+ background-color: var(--color-2);
+ text-align: center;
+ font-size: 24px;
+}
+
+.footer {
+ background-color: var(--color-2);
+}
+
+.footer-link {
+ width: 100px;
+ height: 65px;
+ background-image: url("assets/images/resize_logo_3.svg");
+}
+
+.fade {
+ opacity: 0;
+ transition: opacity 0.5s ease-in;
+}
+
+.visible {
+ opacity: 1;
+
+}
+
+@media (max-width: 800px) {
+ body {
+ font-size: 16px;
+ background-size: auto 90vh;
+ }
+
+ .heading {
+ font-size: 32px;
+ --webkit-text-stroke-width: 3px;
+ }
+
+ .button {
+ padding: 18px 32px;
+ font-size: 16px;
+ }
+
+ .heading-orange {
+ margin-bottom: 24px;
+ }
+
+ blockquote {
+ line-height: 1.2;
+ font-size: 18px;
+ }
+
+ .schedule-day {
+ padding: 12px;
+ }
+
+ .schedule-time {
+ font-size: 16px;
+ }
+}
\ No newline at end of file