diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..d85117f61 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,24 @@ +# Use the official Node.js image as a base image +FROM node:latest + +# Set the working directory in the container +WORKDIR /usr/src/app + +# Install Firebase CLI +RUN npm install -g firebase-tools + +# Install Hugo +# The following commands fetch the latest version of Hugo, install it, and clean up +RUN wget -q -O hugo.deb https://github.com/gohugoio/hugo/releases/download/$(wget -q -O - https://api.github.com/repos/gohugoio/hugo/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')/hugo_extended_$(wget -q -O - https://api.github.com/repos/gohugoio/hugo/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')_Linux-64bit.deb && \ + dpkg -i hugo.deb && \ + rm -f hugo.deb + +# Copy your project files into the container +COPY . . + +# Expose ports for Hugo and Firebase (adjust as necessary) +EXPOSE 1313 5000 + +# (Optional) Define default command to run your application +# For example, starting a Hugo server +# CMD ["hugo", "server", "-D", "--bind", "0.0.0.0"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..5f66bc3ae --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "name": "my-project-devcontainer", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", // Any generic, debian-based image. + "features": { + "ghcr.io/devcontainers/features/hugo:1": {}, + "ghcr.io/devcontainers-contrib/features/firebase-cli:2": {}, + } +} diff --git a/.firebase/hosting.cHVibGlj.cache b/.firebase/hosting.cHVibGlj.cache new file mode 100644 index 000000000..f3e456f00 --- /dev/null +++ b/.firebase/hosting.cHVibGlj.cache @@ -0,0 +1,319 @@ +blog/2023-12-03-apnabanale/index.html,1701679855146,b1d362b24e1e00d48b8a9ecede696ea6d93b5dadbd8fc2faf4f82f1596c54d75 +blog/solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html,1701677530183,c78edba46d9985c29398376c6664aad7f5068ee5e9cc7afde6e43189eb4b50b0 +404.html,1701679890258,3047810e2f89d5dda685c6b7b006a2a6a44c4feaf58290da023c3e65d9aca607 +index.html,1701679890110,8c16859105295ec862066dc6d3af7abb0ddcd77a1929792b02a81f65404a689d +sitemap.xml,1701679890258,46a8e57fb06f95ae62b11042a231cdb8adc6d4c6fcadedf15c9dc5d15f869337 +about-me/index.html,1701679889978,63e5be8d41a10385327efd184c13291d7637ac0d15222391a4a4d27896a95852 +authors/index.html,1701679890110,a90e987600ffd1d295e1a786d5b2a084fb06e0ffabe6b46528bb19279c568612 +authors/index.xml,1701679890262,a1e63210bcaeb4b6e0541e53f5fc810ebe642f59dfc65b85de112685540cc991 +authors/page/1/index.html,1701679890110,82264acd09b709f76ab6ea699e0056ac843a04ef8a5821709dca9e08774f1d6a +blog/index.xml,1701679890262,ec6e5eb6be7948d36f8e74390448cc633546987b817f0042f42c5b4d1b4c066b +index.xml,1701679890262,d42d0ded3ec315db7bcda048ab0dc958932e7aa230f94bc31498b73d60918b4f +blog/1-on-1-coaching/index.html,1701679889990,68fd728467c9842955307c4600dd63e4fc1324084a99b590d6acfa06780bcac6 +blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/index.html,1701679889994,ce8af9467c32aa0b816a6717b8bc481310c0d4a098ae5405ad8ffe43b7b0484c +blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/index.html,1701679889994,f20723d8bbe0850157fd8964799f96d3e00bc2306d681f4f32b135215710b78f +blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/index.html,1701679889998,562292d21409cb9f67b77b0595ea772b71a9586e0a76bd7aaf03967f2e4973ef +blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/index.html,1701679890002,6b619fb29c0f1f485ad4d6d0068d991fcfc6d0aac6af998c8683f243aed8b4bb +blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/index.html,1701679890002,dc6e6ce66464a899374f74cd4f2c45cdd33fb5ab74dc8fc1242307b4dc43cf88 +css/animate.css,1701677530495,3fa5bbd7e93d93660719190689f9b59af7b9a5ecaf5a0ca5db062b26fa470ef5 +css/custom.css,1701677530495,a2fc81f7796f664cf3c41ff4d9bab34fc3d01f579a3f6c11e2f787bfd986e5bb +css/owl.carousel.css,1701677530495,aff92565dabc26e8c57d33b2f1f808394b9d434b8d489fa30c5d1ff6360da30f +css/owl.theme.css,1701677530495,51d5afbf0148ea7a2ef8cc079618a8651ed3960d5ddb486ece82811ab1a2b6fb +css/style.blue.css,1701677530495,b3ce13e519e7720ebb346fabb16e87315b0ab4f1d4c24a728029b21ebed7a5a7 +css/style.css,1701677530495,5692148637d386bac97075e3cd614995e41be626199f97751d829a19371c6d1d +css/style.default.css,1701677530495,531dbf0f5a9227ac0adc94e613b2baf2ab6efabcc84c400317874fced0281406 +css/style.green.css,1701677530495,57250037da41b3ff5a3229b4525f3e205a88e724687db352f5b908587bc8801e +css/style.marsala.css,1701677530495,5bbb3c48e95ba4881cd874ac845d711dc75e873449abe1fec698e396d0aaad22 +css/style.pink.css,1701677530495,7bce2ded4f776b5bd5520249e51a6458c10549766904b4cb64f5cba86d4f17c5 +css/style.red.css,1701677530495,a53f2b5cf75148d0e77110c0f879086b62930dbce80fb7ad94f7cd40576f42b1 +css/style.turquoise.css,1701677530499,46a3a733a8fab0b94a4a1ae10b1bd8f247dc2b5b6abbebe5e8c14e494f096498 +css/style.violet.css,1701677530499,7130810692e4cfd95ccfe52a955b552b2965ce501ec608d51e77f119212b5651 +blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/index.html,1701679890006,e1c4ada74c85616dbde3fc28ea1e24ec9a4ae0bdd0fb017a0bb9fb8071bc0b11 +faq/index.html,1701677530191,e5e46aec8d5be54b5f7e778577fe05d69bf5b8aea82dae772f2b8378591604d7 +images/favicon.png,1701677530503,4c7ee92e4a1de66784c8e56123ec67fc959da25a8ae0fc27f6b09dec7c2e96c9 +images/pin.png,1701677530519,1e2f6f86b29481eadb798941755ef883b334ce099301d2125cd62bc0132d8a14 +images/about/about-content-svg.svg,1701677530499,ab4940a5e50b7e85962f41579fafef466b96fa41bb5a8f0e711a15280998c797 +images/about/about-img.jpg,1701677530499,a11f615c5a64201e657dc270b50d56f2d41ec51b94fac29627fb5a4d432a6dc0 +images/about/about-mask-svg.svg,1701677530499,e5d8be40a49eeb277d7d863a9465b3ef604ac1e2fe10d013399424c5952bd3b6 +images/blog/blog-shape.svg,1701677530499,f7a287e05d0e1a7f75a8aecfa70e192558cfedeefd10f5d066978ffad0202bd8 +images/contact/abstract.png,1701677530503,022fd0ea2c07f11fbfcf24c522d4a3a28929ac76f9181491a93d9ec04233502e +images/contact/github.svg,1701677530503,b6ce55369095ae0fad9374d6d42ee1b60599d39c07f94a8b16bd8a5de0650503 +images/contact/leetcode.png,1701677530503,9331b1ced4855331de9b953d4740cfd75cfddaa30d6cdd424f1ab5bb90900cc0 +images/contact/linkedin.svg,1701677530503,a8d10a267fede7f8e13df40fdafd03bdcb0d33897dd9ce480fd383e7f45d2db2 +images/contact/location.svg,1701677530503,c03394df550e467f0534b429712225b4dde160cf0184a5c8adcd5dff37cf155f +images/contact/profile-img-small.jpeg,1701677530503,88a218e3b8e021c7982a0317f16c27009eeff47952aa404e915bb7a15d62a7ea +images/contact/resume.png,1701677530503,bedafdaf24517590963cdd0eeaf521834e05a79baa47feb27ca5e2e47edcd447 +images/hero/bkp.jpg,1701677530507,2420b225d55e8a993feb0e7d875090100b0bf09ebae45d57087dcb12bfe67e2b +images/hero/chicago.jpg,1701677530515,92f197bce5a0f8837fce1f4db18667a1a86dc547de0caa96952523cd23041cb9 +images/hero/figure-svg copy.svg,1701677530515,f6903da63ed598b3970a9d44dd5bdfc4d091eb41e023031832dcec66dc2db3c9 +images/hero/figure-svg.svg,1701677530515,23f2d4411c246cf6614705c9611dbfeb4ee757fe8884889727a723c61b37592c +images/hero/heart-svg.svg,1701677530515,c59fe658609a6d2dd5b69104c39bc53e79d55b6c0a9e969f56712f89c8987b86 +images/hero/hero-background-svg.svg,1701677530515,997d45b404044c5423ace8d63ce2ba649a0272f6a687ac870f36a5367d2ba141 +images/hero/hero-mask-svg.png,1701677530515,e17b32c40ec903a51083c46cd9f51d128ce68ed1d229602f9a6893eca5b71e6c +images/hero/hero-mask-svg.svg,1701677530515,42b9cb8568b7c7550b1141663b179f919aabcf0b4ef360a53c97146400557fc6 +images/hero/popup-thumb.png,1701677530515,5d28d2258dae9d3fe2aa41fd94f431a7c7dc57e5acfde0fe46ca2c09523e7296 +images/hero/signatureupdate.gif,1701677530519,fedf1f2e7c25162aa2493752cb1f7b0038fe7ad1f0f5c08ef0e87f305c5ed1ea +images/latest-post/latest-post-1.png,1701677530519,7c53743211d27c3a60a229d413f00975de98d1ebb63e03eaddc160a09f659952 +images/latest-post/latest-post-2.png,1701677530519,1b9673b03f5fdba4a5483877682be1de0f2434cb9db1171a1fa7db8b5cabedec +images/latest-post/latest-post-3.png,1701677530519,316b792a7be3b423eb04dd04e303c65dc2ff4562a2c16f925f160e987b481697 +images/portfolio/ProsperLoan.html,1701677530539,55ff7bb97888c848f7f2d35d2e467b42b2d28ace5015695f951a99b0bc24a967 +images/portfolio/case-details.png,1701677530539,f201b7aa18d7255136176108ef1bc8762aaa4c861c5c502fd0e08d041421a246 +images/portfolio/deep_learning.jpeg,1701677530539,b178c97a7e112d19d4449ad6cc4c17f50dc406d902bb00a1cdde6e65f6b2f9e9 +images/portfolio/predictchurn.jpeg,1701677530539,9c8e4605cf58a1dfe6b1b4b9a67106d17e8454fc92eb25d24aa09309a33d5853 +images/portfolio/seattle.jpeg,1701677530539,96bfa4a75861b9fdf0b09bdeb03ae9d65c345c76af02a8f4b371a8f1e65a5345 +images/portfolio/stockprice.jpeg,1701677530539,b8c508b5ad85417841a7e62927216425422d0f57a1bf7be69f4c5fcef146dd77 +images/screenshots/aks.png,1701677530543,32ea3df35d420ab07e910541711832a0a653a79258fc80bfbbe18a789739d2b5 +images/screenshots/debezium-connect-cluster.png,1701677530547,b15ede0999c24b2df2ba74b9aa7adca68bfb26e65aa9105347fa89b925b061cf +images/screenshots/load-to-df.png,1701677530547,7d006fd696a2befeb99226a349378671713096da7fdb064a46d92b1290580962 +images/screenshots/python.png,1701677530551,c7150d1553554c1ce08051f8da03a27df7308b9e48d36539345000fa51e19fcf +images/screenshots/scala-solution.png,1701677530551,c9bfa34f544352d92f7b04400c02a379634f0a50f1c939c7b40c445ad5a6f876 +images/service/background-pattern copy.svg,1701677530551,73c1b88c6583a7cdfc527e5963c8a533f6335d6468dfb8b7686682ce62d97e60 +images/service/background-pattern.svg,1701677530551,dc3925c85de24bcd78348b5d6b11d7c5e7d420d779956b229b64a5d47d7a2a59 +images/service/background-shape.svg,1701677530551,d78d1a9b594a094bdd5a8ae2ca5a3db909b3ce0eb3a7227246069f1bf3ac9a20 +images/service/branding.svg,1701677530551,ddbbeab45d57c753c9a6ccaa3a43cf18fad4358f2d14a0c9cd4ee3b054b3ba79 +images/service/creative.svg,1701677530551,3772583497892824f1e1349e401ac22d924f974d8199fb93eec94884c17d24d8 +images/service/ui-ux.svg,1701677530551,1031fbe684875581cd19adc8fa606d115614c50074a6d2b08f6569e0b83d237d +images/service/web.svg,1701677530555,64afa594d08d63602d0076a69eacae18466b480e0621809903a43713ee9589d6 +images/single-blog/aksdbz.png,1701677530555,ad6c86df53f7acd6b66b62e7093c717c9174b8f7c498752a0272512e3eba142b +images/single-blog/blog-img.jpg,1701677530555,4a28ed121ab6267c48cc66708379d294efded56998a7f6e7bb2aaf5a0eb26dab +images/single-blog/data-lakehouse.png,1701677530559,762f1146b14d83d3313b504a9a94cff80a32a7a37331f77f098313f78e84ac00 +images/single-blog/feature-image.jpg,1701677530559,613bc17e200a4a86c32d9d2f962bcfca94e6605f585685581f98c1454cae4e3b +images/single-blog/fireworks.gif,1701677530567,905053849681cc555651831a06d091b53055da2fec2610e21b5955b9efe928ea +images/single-blog/git-reset.png,1701677530627,be724bf527d523b552b43d800002941cee912f32b0509f28515457ac8be5a8dd +images/single-blog/microsoft.jpg,1701677530635,484a23ea096dcea3d53aa0e1342910262ca95e43fb20032ab0576c45817eb87c +images/single-blog/pyspark-synapse.png,1701677530635,c8d64f6e9305dad3d5ae604e1f11a4fa80840eb0134d5832d6882bc25ed9e47f +images/single-blog/pyspark-synapse.webp,1701677530635,dedcbb21a9d6a73da4a590081bf6a963e9a8defa88c02b32daa8ed2f8e523c80 +images/single-blog/query-performance.jpeg,1701677530639,46e2f8c5a1e76a578b7ecb34f40dd85be0e820f901797ba4d1cd5c0c59545ca1 +images/single-blog/sql-pool.jpeg,1701677530639,d7cb696dcbb219ca081f98c4666c607d4bb9567008de12c2d7c9b4526e6dd57d +images/single-blog/fy22/1flipfest.webp,1701677530571,7005406cead987589e809a96c86b79bab6da82cda99b5798eb865552151813d6 +images/single-blog/fy22/colchuck-winter3.webp,1701677530571,ddb616b8baa70c2b84e314ef20c829f83bf0976c437901cd402ee353d109a488 +images/single-blog/fy22/colchuck.webp,1701677530579,b9a2fd3f1d9c40471d8d3c857a820881aa4b6e9c9a088468f890e295f27d4627 +images/single-blog/fy22/google-cloud.webp,1701677530583,a56ee5d479a1c508377b7b2ef4e666b85a379d45e56137dc3b3ef1ea1f660138 +images/single-blog/fy22/lake22.webp,1701677530591,d402d89fdef494a6fa99ac6b6bf7224df509e43b13661814d2142a0c7bfcfd4b +images/single-blog/fy22/painting.webp,1701677530591,9f1ad69d0033f445d50796c6627f030bf00cdf604c8afb4fec8b73b9b4098c0a +images/single-blog/fy22/paradise.webp,1701677530595,a4d35deaba8f1cd222a9b18b51e6bfe5528553cfcc4c2e98c9fce003b61ce98b +images/single-blog/fy22/rainier-paradise.webp,1701677530599,0684fc77880df196cf88de96ed5866c2423168926052e72d73a17ffc1985bd4c +images/single-blog/fy22/rainier-sunrise.webp,1701677530603,97a162998fa1222990ab04cb9fe47845b9ac6b8535e7fdb7aac1bff68e6a67ee +images/single-blog/fy22/seattle-by-night.webp,1701677530603,bb6af95465f1a277a9de7413085979e78cf852af2635099e0fa6f0e90b17ad65 +images/single-blog/fy22/skiing.webp,1701677530607,9bc0833f41222d604bb494712c4fe6e123520c25777d49fbb40a50199275415b +images/single-blog/fy22/talk.webp,1701677530611,d98cef1781342d8d15bb41080ecbc7deaad92f52431b71e02507739316179d36 +images/single-blog/fy22/talk2.webp,1701677530615,7ecd48326a5105ea38e301debacf0be3d0d5e02ecc3cd1e732e9ea75f3b62326 +images/single-blog/fy22/team-outing.webp,1701677530619,a01d25842e0b61e47ea01e10c9654f9f6853b4d34d4abca95c4bb56ad71d1e3c +images/single-blog/fy22/treehouse.webp,1701677530623,96c5a7b8db1452da176c379817c77a23d8a2a3ce1ab5b381cabd75546c385538 +images/single-blog/fy22/winter-hike.webp,1701677530627,9efce0883d01a42577d3351aff62d1e696a46e685a29ecc97d1c465ef3141df4 +images/site-navigation/logo.png,1701677530639,7b9c924e5821cd17aa13b1fe33336024026cf6856aa5f52892149d08bd60642c +images/site-navigation/logonew.png,1701677530639,3414aeae2d18ff3b8f3252d663855f7b908744586143c669f0490fa45eef9a26 +images/skill/myskillupdate.png,1701677530695,1425ff5926d4d78bbbf578a32b67d3f18cf7171af4c3deb2e1331ca412fecffe +images/skill/skill-background-shape.svg,1701677530695,bee9a543d4e296aaa22c90b1836db313ca11d0451a57abe95583dff756149bd2 +images/skill/skill-mask-svg.svg,1701677530695,fd1b7e41865154f7950de95d6df4731a242c3d25f3fd0b7360f28c4f98b2524a +images/skill/skill_image.jpg,1701677530695,953b677d5e83a52b6f526fe4d7c412d1c4077684298ed629d06fdd776f5e5862 +images/skill/skills.png,1701677530747,779c24d381308d5a6ac14e3cbba0068bba64f6e58a4e0015dc608f28eb5bedfd +img/Canadian Data Guy-logos.jpeg,1701677530751,75661c0f7efd92089d6b534642bc100e3570c1c1379ab9a347eb45a4b4e38546 +img/Canadian Data Guy-logos_black.png,1701677530751,744c56785af14df44e50656d4782722c6968ee100575c2e959c99120f2e64f0d +img/Canadian Data Guy-logos_transparent.png,1701677530751,1ef320cf7eeb38ccab2163222e3cd81977da53cc8d39c7b7ad1c91ebc7f1827b +img/Canadian Data Guy-logos_white.png,1701677530751,964be8c54abd1545ed88739636a362d7d05a7e91cb2bc30aaf49c55d8fb9e86d +img/apple-touch-icon-old.png,1701677530751,169cab47c6b301986c09aa68bd45b1468795071fb7350719ee4e4846d9fa38dd +img/apple-touch-icon.png,1701677530751,064e4d668d3ab395638f9da50dcd3ab24d0bc81dd89f88992f939449431493d7 +img/avatar.png,1701677530751,9624a33bcf9080a1989e6746b956ec39cc8fcfda73dd375a246bf79f7fc238be +img/banner.jpg,1701677530751,3b129c3a5dddcc9f0095d7be38f33643183b4f43940799699e31ce5f7cfa37ef +img/banner2.jpg,1701677530751,d76eafe548608425b76674dfb2bab4c9942652b912a983efea444ade5646bd0c +img/basketsquare.jpg,1701677530751,66dae45a880cc73f8d47d70442c898d1026605a77aef275cb3bdecb1346dbcbd +img/blog-avatar.jpg,1701677530751,c2cf7fc45cc5e0fe0c462a0349853f7ed268248e1ac5833f16cbbb7289de3ec0 +img/blog-avatar2.jpg,1701677530755,63f23a1c26abe6997e04e777fa5d6c2d235f9889b5ee831e142947f841ffe799 +img/blog-medium.jpg,1701677530755,db73f8fc11f22292080f91d0a52c387cefb5007c131b40eb2416a5baa17f1dab +img/blog-recent-2.jpg,1701677530755,206918560227a2fa8eeccf6d8bfd1aa09220038a6ad998e849c4c9153154e45b +img/blog-recent-3.jpg,1701677530755,594c1d0d9a8ecbf567d804229f17f3a624e6cd301ceea2f0df2399cf8592a2e0 +img/blog-recent.jpg,1701677530755,163a5e2491b2a947d061ef159f7254a1e39bad25fd3f15b7eb44e6d0d726c779 +img/blog.jpg,1701677530755,800cab791db380ed4be9525539bfc2198a2bf6e9d08e8794a31985e1f1cd3a5f +img/blog2.jpg,1701677530755,a1d80afdc9bbc4c0b4e2ca781677b1ed713fab7e753e28a49022153965f91619 +img/cdg5.png,1701677530755,cc3ba9d2663f8846cbe7a1fb2f93d99ace48316f415a8540e5c84e0fac7b21f0 +img/cdg9.png,1701677530755,df6c0682cd728364efb1a68b804f6545cc6a7714b2f9eb55c9e22ec193a8031c +img/chicago.png,1701677530755,9624a33bcf9080a1989e6746b956ec39cc8fcfda73dd375a246bf79f7fc238be +img/cpg3.png,1701677530755,d3ee52fadeacc960d05c8a5db2e9a632805487a27851443822cba17aa29cde46 +img/detailbig1.jpg,1701677530755,398ffc2068c68570038e56a4dde185032b2220131cb2b2b5d65bb1dc36bb92ec +img/detailbig2.jpg,1701677530759,8bff0f7839f3516a0e5e35f7fbbdf073b80f8e67b54cd2e41c607b8f3e089abd +img/detailbig3.jpg,1701677530759,37de946740d9ea9cecbaab9f48c2bc39e143e61bb0a37e90d06683ffbb0519b1 +img/detailsquare.jpg,1701677530759,d5c15413de048c85faef387bb55319c060b005c4d33f94c6ca5904eacad872bd +img/detailsquare2.jpg,1701677530759,08df743e5fa79ccc16fa1d4e2461af92182f107effa4f685a065af32cbbfe6ef +img/detailsquare3.jpg,1701677530759,ef45fd75a6dd0d19c8083965600810041128a58749b403dc32da0926b50c6549 +img/favicon-old.ico,1701677530759,c33a1fc5a1db1a521d701505037d7d8940511fe312715e5d62f6a17f465538de +img/favicon.ico,1701677530759,ac40b0151331e3e29651d6a9775aed4e043c3e032020ab01bcf2eac19af57870 +img/fixed-background-1.jpg,1701677530759,acf8cb2c0bbe15022f3a4c835b27e5a7609bdb0b8b886e67ef92400087710f84 +img/fixed-background-2.jpg,1701677530759,8f126e17ef3e32b0409d206bf6f35b715ce53a913fc6264734c78d363de2ee4f +img/grabbing.png,1701677530759,50584ab7861c70b52794d21dc05a49048609ce405e35182bc6c3f92129a7fe93 +img/home-try.jpg,1701677530759,f4d516eae77d8b034e189c4dc87f93a61c128a91a0283bb767bddf36901b1258 +img/home.jpg,1701677530763,8af1e7ed971c2cb76c324acc6410a5ca9f8e9bef896c5047746921cef03c5a1d +img/homepage-slider.jpg,1701677530767,daa7032665b6cbbca66e86be766d7fe8e5c9434371708bed07bbbff68d4497f8 +img/logo-small.png,1701677530767,6483c9cb65872e297a247e703106bc9f30c0d55c741cfc7087044f065fd8c73e +img/logo.png,1701677530767,1603537fdc6c0abb5135878e90658db74dff3b51ea2e636be854e0e0b4718bd3 +img/main-slider1.jpg,1701677530767,79ec1942027f9cbadda8844af4ceaf7a0e44781121070bc238558dd1179902ed +img/main-slider2.jpg,1701677530767,9525ac7cdd37313c7f012018adba1fdd250733342edf94f0409c0b2a61f1fc57 +img/main-slider3.jpg,1701677530767,0dd22fd7f11bc90b4373ca0b394242276ba71d6919ce9ba7ffe5a2a2b9f59c0a +img/main-slider4.jpg,1701677530767,ad99830f5cb1a8417495a676e03ce3cbf4bc57b24e0526fbf5380a925f6c57b2 +img/marker.png,1701677530767,a22e6222e673c7fd9149634c76e1a65f445c2ea618da7c23844c510ecae00515 +img/men.jpg,1701677530767,697562870b38f20fd333a59b80d896b753ba121ccf13502b73eea866660b04c3 +img/mp.png,1701677530767,7c494f7ef90be4a07e5bd602688cebe480daa2d3443cde0ed20c340f78d5615d +img/page-1.jpg,1701677530767,43553712f330917f01c0f912c011f6f18facc570e43888d6cf2b5caf9e18ddda +img/page-2.jpg,1701677530771,7df93b3267eed981547a67d39c7c68fbe1efe2840b00146333eab3f097af4f44 +img/page-3.jpg,1701677530771,36efd1f617dda7c15e74c78088d0a159f3f8c291f4ea38a142390f66d9e439e1 +img/payment.png,1701677530771,bc6a239809f8b958aec02165688947fb7b57ecee8388a59fc1997230ae1306ad +img/photogrid.jpg,1701677530771,315a320cee476271eb8050fae17e6673cc2f6ef377f2d7d13d844732ae8d0b2f +img/placeholder.png,1701677530771,825c3f905b002a5f1e2f913b01bead706c7e9a1288784a4fc92e1d924460743a +img/portfolio-1.jpg,1701677530771,51ed1aa855eaa3c004e080d5923c4e76983bd0f618e9dd35874ec910daed6ed0 +img/portfolio-2.jpg,1701677530771,3b47b5e1c747c416e521572e65fa9fcc2cd8e3057ac6f0488a726b5bb06c7c60 +img/portfolio-3.jpg,1701677530771,aa81ade4b8909433bd23df2336bf9ff39923b0f3265ebd59b55271f9d5fc5307 +img/portfolio-4.jpg,1701677530775,8d00df1690b5ef63099e479810bebd310368e5df5ab4ea3c3d5ce590ec138e51 +img/portfolio-5.jpg,1701677530775,264a949fff61eba26fc29a06325995502e44fea24e15f7c0bf007a6987acb3ed +img/portfolio-6.jpg,1701677530775,4fa2bc13d3ea97a9df5e285b6bfbab6121e41e175815b85baa8dea6545a1f913 +img/portfolio-7.jpg,1701677530775,b412e0888938bb07788bed8637e5af5bbcc7cb1b4ebc6a164939513afe5421a4 +img/portfolio-8.jpg,1701677530775,bd05149dfa5c55c19a09acc2472f7ac23a81201ae3acee87f06a551eec7d180c +img/portfolio-9.jpg,1701677530775,7dcc3786db7554688e1376fcb36fd60392f7be1268967ba1eab480f64f8571e0 +img/product1.jpg,1701677530775,bc180e5d5e7f0870a3773b5036857ed26127d4c9e9b25b614ef0c32b957348c1 +img/product2.jpg,1701677530775,eb1d1bf92339a2963147e66975b33a9bfb9d89369d222757535ef41793f59866 +img/product3.jpg,1701677530775,bb498bfb976ea89381f6c0d5919e7175d4d2aa7723efe83b03c61b22578eb3a7 +img/product4.jpg,1701677530775,0d69153476f7ec1aa82ba5c66ba9d059c6080e64f9c5df687514817e2f3982fc +img/skills-small.jpg,1701677530779,9f938306591b0124a5e029a2455837653bc492f9fda21cdc0d2fdd0e6a39f42a +img/slide1.jpg,1701677530779,5de3e10076102cb596d5cdaa7ec0136e58ad089eafd7c8ac231d541504de7396 +img/slide2.jpg,1701677530779,4867c9bd327645c62b6eed2f662c0b844fa61809a1187ead6b45c27dc7739983 +img/slide3.jpg,1701677530779,98f558da3cb14033c0b29f9775da313fe73135eb105731d05e5d8959ac10c93e +img/slide4.jpg,1701677530779,420a89bf6edd51e33c1164bc2187dee95f818cda7e0e7bc75eb3b90da0574bd6 +img/slide5.jpg,1701677530779,a6c996c10cfc68df08ee6021bc4af4218867c6b3a0f55454fc7a76d5e0facb12 +img/slide6.jpg,1701677530779,5de750738bdc4524b254e76ce0fdea3cea90304d768202a149a4209c2363bdfe +img/soni.jpg,1701677530779,ebdb5017bd4bac6b76669261be00f012fc21c03bc5027f6719ac31f8b5cabee1 +img/texture-bw.png,1701677530779,710e4f3af5d1d9eb330a6e8b9115849f2ed3b752e0a39cb9ac3503f6a0fc823c +img/texture-green.png,1701677530779,f0003d1e3c2214a1261534d17413b01ad9604b147378724d9830bc2e26bf7ec9 +img/texture-turquoise.png,1701677530783,e4b15db46c47f2658ee4749ba0f1452456fae907a788022473619ec99376545b +img/texture-violet.png,1701677530783,2a123fa94499d5fa4db700766b365cffd622e7da3edabb0372db16fd86b23a73 +img/women.jpg,1701677530783,e80a2f8fcbbbea10186c8de20eb9e44e4cc775fc7941da4525c611f77e304376 +js/front.js,1701677530783,773177251057a223a4e2a685e4c30c885da626abd860716ba1abafef1bc6b74e +js/gmaps.init.js,1701677530783,17af3290bf2df6fa8b610065a55e34d0a984bae66091104b6af97045e3d9edbf +js/hpneo.gmaps.js,1701677530783,a6da5e1faaced7b6a94c34b77c213f13fd4631e569c51b4f32b062accf93ac4b +js/owl.carousel.min.js,1701677530783,ecaf9afdca380dc0f5dbb791a7851989b361679eeab232a5f9250bbcf6a7a99e +js/respond.min.js,1701677530783,d6b7d4016fd62012b38c53a33b9370564954524be9fc54b3b01f2d09fb21ff84 +blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html,1701679890010,bbf8143d7ebaf8dfbdd746b3d0248cb6f671dc9bb7ac1cdac3746758e7382734 +blog/arcresources/index.html,1701679890010,7c6a15b591c6f15a45c53dbd855a44633d669e676b1fcda1e53a0c415689cdbf +blog/audantic/index.html,1701679890014,0c4d6be08506a0e663155a57b87616a95b4e5c6726eb78fbe6098fba4e8977d3 +blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/index.html,1701679890038,a2d3a244cc3ec6d95631c166ad2d2f60e1a4eb2ff6f4c562fc86da879531c28b +blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/index.html,1701679890034,fa21c06e455b838d260183f76470e418539f0df952d5ec1aa0acc0da5e8d23c1 +blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/index.html,1701679890042,4b370e757920808f447c52b9f81bff21ecaa79a8d9325b6b93eaaf5abae93658 +blog/howtogetthejobidandrunidforadatabricksjob/index.html,1701679890042,114e3296afe705ae9bd0156632f12f8f465879dbca59805a1439953137d4409d +blog/howtoparameterizedeltalivetablesandimportreusablefunctions/index.html,1701679890046,4543fb40fdbec1f8ce7f4ffe504be2e95b0e689d4db0139cf77a4cd9c43fc8d1 +blog/howtoprepareyourselftobebetteratdatainterviews/index.html,1701679890070,f8229152d4948cfecc2422e4ae97e049573a379069d94c61a811bc469da9754a +blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/index.html,1701679890054,3ee008e19fb5a6154d6da78cdd9dfdadd274f005e1511dd2080c262f5dd086f7 +blog/mergemultiplesparkstreamsintoadeltatable/index.html,1701679890058,bd8ba024c63db988121ff660929600b855c6128277fe19e7834986baa45f75a4 +blog/page/1/index.html,1701679890086,77a4d1f111d39e6facd5b2b32be9a07ff6ea5161aa28bd140765bf946d59b398 +blog/spark-stream-stream-join/index.html,1701679890074,26373b2e1be770724fac43da7f1455ec01094451a625a2ad5ecb0360f7fa76b9 +blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/index.html,1701679890078,bcba6c31b77040e0750d052d413ea7343c4daee1c2fae82f28430ee31d6b40b0 +categories/index.html,1701679890114,f83bd9921fded81ce49b1a30e4da2796933ab722d6502e0e28993b96660515c7 +categories/index.xml,1701679890262,b3637844530d9b4e1d34cd666b2cb0f5b255473277bb82e54b8460523cb07f28 +categories/best-practices/index.html,1701679890122,bc8a8e3e06d874b1401d9a72eb7c8c61f9e799abe6c9a04cd39acb0ca8210e92 +categories/best-practices/index.xml,1701679890262,d4f431fe706afa0df00faf0e1986bb590890efe23b86121b15a9c9e4fa4b678a +categories/best-practices/page/1/index.html,1701679890122,29958c8c731769325cb59fbbd82c4696e1c8bcca9209a7bb00f51d75d6071fd9 +categories/coaching/index.xml,1701679890262,c7cdef83d8ef882a31b9d4a2211a7d324ed91c7c12c9184acb71b018abf01c54 +categories/coaching/page/1/index.html,1701679890130,ac8663f1166fc763534d0882b4d44604a9842010ba10376ba1392888cb9122c6 +categories/customer-stories/index.html,1701679890130,d00db4de452111014bddc43d8fc39abf4070927079cba7bfee53f6c1f68cfb0a +categories/customer-stories/index.xml,1701679890266,a25d85067c31ce3524df8d08bea0150d6e8a52c0af6b19a45a997647aa274db3 +categories/customer-stories/page/1/index.html,1701679890130,542ed414a906d6e8d6a2ec51febfa5c851dbc785e94879012c1f952451ccdbee +categories/databricks/index.xml,1701679890266,0aa2925a9df0b58d9c6b6379a69f9025d1afc8ac16942b2fdd1d7cc755c62e68 +categories/databricks/page/1/index.html,1701679890134,ca6743e9afc0dea3a31a895fa3ea1e6638b63f8b6210d00992919bc20ce973d3 +blog/youtube/index.html,1701679890078,e91bca9cf3233fdfba69a0a41415829310d0feeb314dfc5de3e8fef21a45aca2 +categories/coaching/index.html,1701679890130,298a488ff701efdf1c6dcfd16ba37fd087f5b516b6c6c28b1e82bba43d59fa75 +blog/deltalivetablesadvancedqa/index.html,1701679890026,ed8de82fb37b43eb5cc2c5fb5b1da4e4172b36609aa551c88267e3a5e4589ddf +blog/index.html,1701679890082,b5f1313dae1760fca4deef2176af14d026e85d30ef272f07771dfb0f0a98bfbd +blog/page/2/index.html,1701679890094,2d4541d799560476085f63a9c41b64e30d67c17d2f2f822e03561ca0f11739ed +categories/databricks/index.html,1701679890134,b00949a0b9cac71fc14c5ddf22a4dcdd5b4fc7ab8f3871d516131d1451bfd910 +blog/page/3/index.html,1701679890102,471d8360484d29da3bd6e05ed4f400c43328b52c68a4c3b374abb0cb1c1ddeb8 +categories/databricks/page/2/index.html,1701679890142,423f4c9db75d80fc2eb94582c276a6864e62d27dbf393170e0d1e1ed80cc2bbc +categories/interviewing/index.xml,1701679890266,f44e27ecbc1eba7541ca59d022accc2b28f973920500a74d68b52f198d5547e5 +categories/interviewing/page/1/index.html,1701679890142,571dc427b2de7f7a936b21106fc4a33fd4dd2a473d28e0fd964f8817dbf493f7 +categories/page/1/index.html,1701679890114,110df69608295a722154eb35df06d952caf0330152e46fec4e103c34c5cc3161 +categories/spark/page/1/index.html,1701679890150,bb4ca78c06959fb4c6b554440aea0d219df41f61f2dc5c6c6a7d85d0f9827125 +categories/spark-streaming/index.xml,1701679890266,0c1eb724f23ad59ae0e0dfac74cc93ec643b3109050a1053900048bbc1595348 +categories/spark-streaming/page/1/index.html,1701679890150,33a9662712154f81e8e791468ad234277c848b2720acfc84d76109366322b550 +categories/spark/index.html,1701679890146,84666acd5235526d076fea361783067a032c15dfc9388c3b8f21f1250c227281 +categories/interviewing/index.html,1701679890142,9c051bba359c3193d8dd7922e3c1459ac9a1bba4d867e3dc3344d89a27f75f51 +categories/streaming/index.xml,1701679890266,c8dfa507f49bd2d483bcf222236a8b4b0f67b737c5a248b28a4c178135c191f6 +categories/streaming/index.html,1701679890154,fa66286035b785f39242876f2139e37cd424dccf8150b6a49a4de897fa479245 +categories/streaming/page/1/index.html,1701679890154,0ec0b3594bb5b7dfa40980fe33608d1192131d2923c09daad817fa15e2a80d78 +databricks-customer-success-stories/index.html,1701679889982,3dbbb1292f0244684583b4e2247f34ce4f0cfd8e38bbb0f67fc208d8025ab882 +contact/index.html,1701679889978,2c675014de9f1fc5f6a2531ff739bdeaabda7e9ae04a398569fcb6e198f42992 +page/1/index.html,1701679890114,01c6e408078ca30adfeff97fc867e43f3d608f2d34281bb68071d6ba9ca1cfac +categories/spark-streaming/index.html,1701679890146,b7bc6f9be7feea71acf7f501e42e094f1e7675a0811c0bfffb635f6a07a7115e +page/2/index.html,1701679890118,d2e7056b001eb949eb0b5e50a478900244399876dc48b447a2209667516687ed +categories/spark/index.xml,1701679890266,6d102e8679f15584ace525738e50664380de8a3d5783cb89df3ebe493d913692 +page/3/index.html,1701679890126,710f5ff5ad85b3edb4a97d93761598d9b94c5add8b79c559c996c858a49f61fa +tags/index.xml,1701679890266,09c6f79aadb33126dc839817ed1255ff7818a29f2953b7ce10fbfebd2e3b8056 +tags/index.html,1701679890166,3d9a8912c2e2d1c3bf09cfda7685461968bd0ae3223fb0c3e4d4ca5f05d212d6 +tags/books/index.html,1701679890158,aa98f64899abaab4398e5e486a58fa7c06ef870e35b8835107200e7c53cf4a3e +tags/books/page/1/index.html,1701679890158,f6951c262391d336af20a19bb97a6aa1f9254ba25f5f4d079398c355f9467157 +tags/books/index.xml,1701679890266,afcf25c391a8bc6321a36e4dbae4a5de381bb61e5c80705c69825a8278ea6973 +tags/checkpoint/index.xml,1701679890270,35879dceaa2b6b15e31a94c9eb03468098659cd78abdeabfe4e5b427ce2ebae6 +tags/checkpoint/page/1/index.html,1701679890162,7420f84adef248fcc2b03c2b4c86b6f005bf8a76552a87e8f5d92af00b3393e7 +tags/coaching/index.xml,1701679890270,5432deeef8e8f5f3f62670b018d7a10f8ab2a5a8162faa24d7aeff8b19a782c2 +tags/coaching/page/1/index.html,1701679890170,ba1baffcc43fc6ae386692862f746514090dd0caefd3ec8f8da411701fb57f3d +tags/coaching/index.html,1701679890166,af4a85b4db5abb3edb83d947e8cb89da240d688a13bba78ea1ffa3c9359d2923 +tags/concurrency/index.xml,1701679890270,6eb031ff47c9671a30f1548acf509656f3f0e288e158e622dbee1ba339e4c390 +tags/concurrency/page/1/index.html,1701679890170,799caf059f60e3b2038875893b7edd3d7e059e722f11dedf5e8caf4ade5e6287 +tags/concurrency/index.html,1701679890170,e6af8b12291bf7810df06ff58c5ee9672c6852ae035e3133506c0fad68412a27 +tags/cost-savings/page/1/index.html,1701679890174,853080ff8d348d9c9c3667602dd4a06d8adc1d4efae0ae11790e1e7ff408e050 +tags/cost-savings/index.xml,1701679890270,26b4d222a6873ef2bbad5a873a9345b8e6e2f59d558b3284ad72c8ac0f87a3b4 +tags/dbsql/index.xml,1701679890270,b9d3d1e4a1996b124a1784c53713247f53e01780d4651b8844c4ed17f2948434 +tags/dbsql/page/1/index.html,1701679890174,0d261ce95f7b935520dbef34b6c110a2840bf806c84d67c91d1147ac98c8e118 +tags/cost-savings/index.html,1701679890170,fd31f36fa95477b75a83cfa6504d521928748adcf79563be8b67bff68a9273d7 +tags/dbsql/index.html,1701679890174,e049b47ca64c18563e5e49648a75339c25a2727c67048ea84da5a16bd13b2442 +tags/checkpoint/index.html,1701679890162,36f147e71d71d8c3a6462aa8c4b33b9a5a32fc1f5aa70febd8651203163eda29 +tags/delta/index.html,1701679890178,b9dce0f7a3d5c5e7f09ec5aec7430c5bf607a1a96855487df911e2132c2beb64 +tags/delta/index.xml,1701679890270,877586df0576000251f753c83abedbfa7beba55a94dee208f526704af15344c1 +tags/delta/page/1/index.html,1701679890178,556ac610ba3243642cbbeff349fd35487b4d25da6a37ad1151b8d3ebd5954795 +tags/delta-live-tables/index.html,1701679890178,bd52cf11392a0f4a7cfeeb0829cdffbfdbd792ef6a4f9bb2016ad7f433146747 +tags/delta-live-tables/index.xml,1701679890270,95d3e6cddc98e928f1dcbfa6ce84a02deaf35d58bd63f496443c73b634b8969f +tags/delta-live-tables/page/1/index.html,1701679890178,8563eda6febc2d7b494471e8ff1191448a30936d478e078cbb110967004389db +tags/foreachbatch/index.xml,1701679890270,365e943d50f022d5215f66b2662b4d2eebab155ddb70f47b2aa9c43cec69031d +tags/foreachbatch/page/1/index.html,1701679890206,a27aad973bfc33b65f4f6729aa3c84d329b47e23cfa9c934d42088cd2b2864a1 +tags/foreachbatch/index.html,1701679890206,283901f189446ced3a4f06140617e3cced7e852e23e6c82497c292ffd805baba +tags/graviton/index.html,1701679890182,f78931f74a6f191c4e10e69fa259ec2910371ac133aa5cc28c08908f4783df77 +tags/graviton/index.xml,1701679890270,0e96c0ae4a337fa4f48fd3292c5510da2222743945e6849f27a9b4136cb0329b +tags/graviton/page/1/index.html,1701679890182,cc0cd378a8235313520005f12d1d75d96e1a8578acd01fe9e62e14df22ae52b2 +tags/interviewing/index.html,1701679890186,8b15bc4d65153475786e4b2d29d957ea47ad47b582c4e12774d3336b83ae5490 +tags/interviewing/index.xml,1701679890270,325311de4863cd27c6e531d2b4ad6a8ce0e11d9a102910ddce5fc850e1987332 +tags/interviewing/page/1/index.html,1701679890186,8332ccfcccbd095652f3e3cd3dd42dd05d48ab147bd9794914756e73aea98b40 +tags/job_id/index.html,1701679890190,aad625f815f7b1896acd7dc078be33c4fe4fc14c21c115a5233fde77fe3ddaf4 +tags/job_id/index.xml,1701679890270,23292e36182e9222fdc7e8e0710fca825c085b66ad5a94426ea0f92c09608d8f +tags/job_id/page/1/index.html,1701679890190,a8261368e057a7d96f18ba63b96683269405e78e2dfb810dbd9ca1067061b1fa +tags/kafka/index.xml,1701679890270,ae7b7e558887e06603f906032459518bbc014b88e433ef7376b0a8a8057ade80 +tags/kafka/index.html,1701679890198,1df5b2bb17f6dd1ab59a083152aab79e23bbe13f1e6ec364070ed971ec9782aa +tags/kafka/page/1/index.html,1701679890198,ae55f850928ce380fc5a1bff297235300a725bf31f2c3ebe13fa42741e16f4be +tags/merge/index.xml,1701679890274,e9c6f0861d6d5524dd63d5536ac399cdfc24c98ebaf614cb1d22535e41ccf2bf +tags/merge/index.html,1701679890214,29ec6b794ef6a6504d3c5bb40515e96ea9900c23e6b4cd00c1c1ee6007494bdc +tags/merge/page/1/index.html,1701679890218,e8b8bcdc2aab2af2d3fd0a79158fa80e82f52e6913395b345a263bd57abcb7b5 +tags/optimize/index.xml,1701679890274,eca7b254a6dce08006fbf872642c939606af0da57945df3cb21b68012d67c7d0 +tags/optimize/index.html,1701679890226,20f24fc4e01bea147f732a279c186ccd6760fef714626878073708453bac4a00 +tags/optimize/page/1/index.html,1701679890230,9cc1f0030007d3d28def4832f0b042cc0120d34efe0a7a6e1be16ad482b4e69e +tags/page/1/index.html,1701679890166,e82f9cf7fd545a59403a54b63b4d2063a098d5d855aaf4fbe8fabbcc4bc329a8 +tags/parallelization/index.html,1701679890218,c09083e5dc34653e158e00d6412e37eb773d44dbbe62e900c7b4b76da1be12a1 +tags/parallelization/index.xml,1701679890274,b4d88b38b3b2c9c466bcd747ac635f440b3c13ab1554c187de7999446a35361a +tags/parallelization/page/1/index.html,1701679890222,ff28f6071f1ac411cb0977ed8d5acdf3690d5a07b5a44a835708a651048aace6 +tags/python/index.xml,1701679890274,ff5f07286e1eb3fd38d864fac1edc1f8e150ac64decbf8d9a970ec473c1a2261 +tags/python/page/1/index.html,1701679890226,152847e60316fa0548d9fbd8f9c12a5b8273de0de7ea221a5594b96d3d300cba +tags/python/index.html,1701679890226,940395a8c8ea6e1ac2c09afc0d1813b2976fb52e787d99839769310cd927fc00 +tags/run_id/index.html,1701679890246,f54ba004797c9e88785887aa8346cb0c36014aaafb1d6381bb1337cbf3c9e1da +tags/run_id/index.xml,1701679890274,5a0b67fdffb7b32661cf67c5e4623a2abe268832be7aa1dc9e2e38ef74bf4f7e +tags/run_id/page/1/index.html,1701679890250,427eca3424c20ab72245d0df9c66ccb1102edb3ea048f0d8b0c488b768cc9c1c +tags/spark/index.xml,1701679890278,6c558ef2077e712be3bb3960ab5eb1602e7de2d2b2e40e1139b9b1bc83661273 +tags/spark/index.html,1701679890238,cc23a73aed9cadb4dcef2ac349934aa19173c5937bb4d0b4adbcad12f4eade79 +tags/spark/page/1/index.html,1701679890238,3736227b9edbeb2ac979cf394c2dcafd48d71049e39bc786cef28291c7b24e21 +tags/spark-streaming/index.html,1701679890234,3ab1d6ded9bcc60996456be80124336b503596a17f2d19306341aa2706a1a0d2 +tags/spark-streaming/index.xml,1701679890274,eaab108c3be2548fdf43437de96c671759ff58a178d65eab30677b18b6edb90f +tags/spark-streaming/page/1/index.html,1701679890234,225beb30aa89ac305a802e172581c22379995d1f3b6017cfac531703ba71b2c3 +tags/streaming/index.html,1701679890242,74a76c83027df34caa70549c85850d82bb125a6a0749c5ac55c932842a18e37d +tags/streaming/index.xml,1701679890274,6572c36eb83b0f35340393de2940b45f821957c4473a4ac577076e8892e59629 +tags/streaming/page/1/index.html,1701679890242,909ad19526209d5d702e2c4c51c5cd4efc6cba507a4dc643967c811470b45b24 +tags/workspace/index.html,1701679890246,35afc4177452592d1fda084c220101342d4b1478ffb2249045e2d1a145b40f2e +tags/workspace/index.xml,1701679890278,a0c2be1f22522493026697a562033d09e20f2e71c0b0f1c08fe83926d889f829 +tags/workspace/page/1/index.html,1701679890250,9b280b59f40c76575c7c495d5bd9f2d710af87a5641b0d3d802bd67f251dcc32 +tags/youtube/index.html,1701679890250,cf1c569fe256baf60688c066a097dc80d2581559c7a368d1ff697772d655cabf +tags/youtube/index.xml,1701679890278,fba401feb728873b58cf761b1553e759deaf7d3793ad780633ed789cb164a3a1 +tags/youtube/page/1/index.html,1701679890254,ee84bb8bbfab3f319dcb1f3e77955f2e56337f85f3659a8915b6234143fe20e7 +tags/z-order/index.xml,1701679890278,b8ac92c4205d8ae1f14cfa03270aa30c79dbf93683f163202b976a16c71b61d8 +tags/z-order/index.html,1701679890254,f7fbc9605af234c8f20446f48338f43c0ec3c47d68e8deef5ce4a7221e40c618 +tags/z-order/page/1/index.html,1701679890254,d0274f2ee3ee4eafcf4ab8d18b101997ebe99980f83bd10518adbea6160911ae diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 000000000..9b70f7159 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "universal-canadiandataguy" + } +} diff --git a/.frontmatter/database/mediaDb.json b/.frontmatter/database/mediaDb.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/.frontmatter/database/mediaDb.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index e4bb33193..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -github: ryanfox1985 -patreon: ryanfox1985 -custom: https://paypal.me/ryanfox1985 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 5902c1020..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Build project with Hugo -on: [push, pull_request] -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./exampleSite - steps: - - uses: actions/checkout@v2 - with: - submodules: true # Fetch Hugo themes (true OR recursive) - fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod - - - name: Setup Hugo - uses: peaceiris/actions-hugo@v2 - with: - hugo-version: 'latest' - extended: true - - - name: Build - run: | - hugo --minify diff --git a/.github/workflows/build_ai.yml b/.github/workflows/build_ai.yml new file mode 100644 index 000000000..ba02de51b --- /dev/null +++ b/.github/workflows/build_ai.yml @@ -0,0 +1,40 @@ +name: Build project with Hugo AI +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + env: + FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} + defaults: + run: + working-directory: ./universal-canadiandataguy + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: latest + extended: true + + - name: Build Site with Hugo + run: hugo + + - name: pwd + run: pwd + + + - name: List output for current dir + run: ls -l . + + - name: List output for debugging + run: ls -l ./public + + - name: Deploy to Firebase + uses: w9jds/firebase-action@master + with: + args: deploy --only hosting \ No newline at end of file diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml deleted file mode 100644 index 92b274e91..000000000 --- a/.github/workflows/deploy-gh-pages.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Deploy Github pages -on: - push: - branches: - - master # Set a branch to deploy -jobs: - deploy: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./exampleSite - steps: - - uses: actions/checkout@v2 - with: - submodules: true # Fetch Hugo themes (true OR recursive) - fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod - - - name: Setup Hugo - uses: peaceiris/actions-hugo@v2 - with: - hugo-version: 'latest' - extended: true - - - name: Build - run: | - hugo --minify - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./exampleSite/public # if not specified it defaults to ./public. source directory of the built files to make the deploy (must match the build to directory in config.toml), eg ./docs, ./public, ./exampleSite/public - diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml deleted file mode 100644 index 37929bd01..000000000 --- a/.github/workflows/eslint.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Eslint -on: [push, pull_request] -jobs: - linters: - name: Run Eslint - runs-on: ubuntu-latest - - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v1 - with: - node-version: 12 - - # ESLint and Prettier must be in `package.json` - - name: Install Node.js dependencies - run: npm ci - - - name: Run eslint - run: npm run eslint diff --git a/.github/workflows/slack-notify.yml b/.github/workflows/slack-notify.yml deleted file mode 100644 index f6aaa226f..000000000 --- a/.github/workflows/slack-notify.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Slack Notification -on: push -jobs: - slackNotification: - name: Slack Notification - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Slack Notification - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 19ee8876c..d7a1be4a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store -public/ npm-debug.log /node_modules/* -exampleSite/.hugo_build.lock +exampleSite/.hugo_build.lock \ No newline at end of file diff --git a/.hugo_build.lock b/.hugo_build.lock new file mode 100644 index 000000000..e69de29bb diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..c53facf8f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "github.copilot.enable": { + + "*": true, + "yaml": false, + "plaintext": true, + "markdown": true + } +} \ No newline at end of file diff --git a/README.md b/README.md index 44af8c47a..17bd3a477 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -# Universal Theme for Hugo - -[![Code Climate](https://codeclimate.com/github/devcows/hugo-universal-theme/badges/gpa.svg)](https://codeclimate.com/github/devcows/hugo-universal-theme) - +# Universal Theme for Hugo | Canadian Data Guy Universal is a clean and stylish website template built with [Bootstrap](https://getbootstrap.com/docs/3.4/getting-started/). It stands out with its clean design and elegant typography. Demo site: [https://devcows.github.io/hugo-universal-theme](https://devcows.github.io/hugo-universal-theme/) @@ -17,7 +14,7 @@ This Hugo theme was ported from [Bootstrapious](http://bootstrapious.com/p/unive ## Table of Contents -- [Universal Theme for Hugo](#universal-theme-for-hugo) +- [Universal Theme for Hugo | Canadian Data Guy](#universal-theme-for-hugo--canadian-data-guy) - [Table of Contents](#table-of-contents) - [Features](#features) - [Installation](#installation) @@ -31,6 +28,7 @@ This Hugo theme was ported from [Bootstrapious](http://bootstrapious.com/p/unive - [Menu](#menu) - [Sidebar widgets](#sidebar-widgets) - [Top bar](#top-bar) + - [Menu behavior](#menu-behavior) - [Blog post thumbnails](#blog-post-thumbnails) - [Landing page](#landing-page) - [Carousel](#carousel) diff --git a/config.toml b/config.toml new file mode 100644 index 000000000..52f20d12f --- /dev/null +++ b/config.toml @@ -0,0 +1,284 @@ +baseURL = 'https://canadiandataguy.com/' # Configure URL. if using gh-pages https://github.com//hugo-universal-theme URL: https://.github.io/hugo-universal-theme/ +title = "Canadian Data Guy" +theme = "hugo-universal-theme" + +languageCode = "en-us" +# Site language. Available translations in the theme's `/i18n` directory. +defaultContentLanguage = "en" + +# Enable Google Analytics by entering your tracking code +googleAnalytics = "" + +# number of words of summarized post content (default 70) +summaryLength = 70 + +# Define the number of posts per page +paginate = 10 + +# not pluralize title pages by default +pluralizelisttitles = false + +[menu] + + +# Main menu +[[menu.main]] + name = "Home" + identifier = "menu.home" + url = "/" + weight = 1 + +[[menu.main]] + name = "Interview Coaching" + identifier = "menu.coaching" + weight = 7 + +[[menu.main]] + name = "Youtube" + url = "/blog/youtube/" + weight = 1 + parent = "menu.coaching" + +[[menu.main]] + name = "how to prepare for data interviews" + url = "/blog/howtoprepareyourselftobebetteratdatainterviews" + weight = 2 + parent = "menu.coaching" + + +[[menu.main]] + name = "1 on 1 coaching" + identifier = "coaching.1-on-1" + url = "/blog/1-on-1-coaching/" + weight = 3 + parent = "menu.coaching" + + +[[menu.main]] + name = "Blog" + identifier = "menu.blog" + url = "/blog/" + weight = 2 + + +[[menu.main]] + name = "Portfolio" + identifier = "menu.portfolio" + weight = 4 + +[[menu.main]] + name = "Databricks Customer Success Stories" + url = "/Databricks Customer Success Stories/" + weight = 1 + parent = "menu.portfolio" + post = 1 + +[[menu.main]] + identifier = "About Me" + name = "About Me" + url = "/About Me/" + weight = 10 + + + +[[menu.main]] + identifier = "contact" + name = "Contact" + url = "/contact/" + weight = 10 + + +# Top bar social links menu + +[[menu.topbar]] + weight = 0 + name = "LinkedIn" + url = "https://www.linkedin.com/in/canadiandataguy/" + pre = "" + + +[[menu.topbar]] + weight = 3 + name = "Medium" + url = "https://canadiandataguy.medium.com/" + pre = "" + +[[menu.topbar]] + weight = 4 + name = "Telegram" + url = "https://t.me/+12065656859" + pre = "" + +[[menu.topbar]] + weight = 5 + name = "Email" + url = "mailto:info@univercanadiandataguy.com" + pre = "" + +[params] + viewMorePostLink = "/blog/" + author = "Canadian Data Guy" + defaultKeywords = ["devcows", "hugo", "go"] + mainSections = ["blog"] + defaultDescription = "" + + # Social media + default_sharing_image = "img/Canadian Data Guy-logos.jpeg" + + # Google Maps widget: If `googleMapsApiKey` is not set, no key will be passed to Google (which likely results in a broken map widget). + enableGoogleMaps = false + googleMapsApiKey = "AIzaSyAf9JH9E4ZX5cYPmphSN3cY5TI5YbZ6A3I" + + latitude = "-12.043333" + longitude = "-77.028333" + + # Style options: default (light-blue), blue, green, marsala, pink, red, turquoise, violet + style = "red" + + # Since this template is static, the contact form uses www.formspree.io as a + # proxy. The form makes a POST request to their servers to send the actual + # email. Visitors can send up to a 50 emails each month for free. + # + # What you need to do for the setup? + # + # - register your account to https://formspree.io/register + # - login and create new form + # - set your form's endpoint url under 'formspree_action' below + # - upload the generated site to your server + # - test a dummy email yourself + # - you're done. Happy mailing! + # + # Enable the contact form by entering your Formspree.io endpoint url + #formspree_action = "https://formspree.io/sample/of/endpoint" + formspree_action ="https://formspree.io/f/xayzjrzy" + contact_form_ajax = false + + # Formspree form supports Google reCAPTCHA Key (type v2). + # If you use this feature, you should enable reCAPTCHA feature in the Formspree dashboard. + # + # By default, Formspree use a redirect page for recaptcha widget. + # If you use a recaptcha widget in your contact page, you should do next steps. + # (if you don't want, skip these steps) + # + # 1. register your site on Google recaptcha admin page: https://www.google.com/recaptcha/admin + # 2. select reCAPTCHA v2 and checkbox widget type. + # 3. remember site key and secret key. + # 4. enter secret key into "Custom reCAPTCHA Key" field in your Formspree form setting page. + # 5. change `enableRecaptchaInContactForm` is to false + # 6. enter site key into `googleRecaptchaKey` to enable a recaptcha widget in your page. + # + enableRecaptchaInContactForm = false + googleRecaptchaKey = "6Ldc1QUlAAAAAO_4M9cklpnsjsPyUGku5sOZ4BVf" + + about_us = "

We offer expertise and consulting in data engineering, analytics and cloud computing.

" + copyright = "Copyright (c) 2023, Canadian Data Guy Corp; all rights reserved." + + # Format dates with Go's time formatting + #date_format = "6 January 2006" + date_format = "January 2, 2006" + + dropdown_mouse_over = true + + disabled_logo = false + logo_text = "Canadian Data Guy" + + logo = "img/cdg5.png" + logo_small = "img/cdg5.png" + contact_url = "/contact" + address = """

Canadian Data Guy Corp. +
Calgary, Canada +

+ """ + +[permalinks] + blog = "/blog/:filename/" + +# Enable or disable top bar with social icons +[params.topbar] + enable = false + + +# Enable and disable widgets for the right sidebar +[params.widgets] + categories = true + tags = true + search = false + +[params.carouselCustomers] + items = 3 + auto_play = false + slide_speed = 3000 + pagination_speed = 1000 + +[params.carouselTestimonials] + items = 4 + auto_play = true + slide_speed = 2000 + pagination_speed = 1000 + +[params.carouselHomepage] + # All carousel items are defined in their own files. You can find example items + # at 'exampleSite/data/carousel'. + # For more information take a look at the README. + #enable = trueMakes + auto_play = true + slide_speed = 2000 + pagination_speed = 1000 + +[params.features] + enable = true + cols = 3 # Default: 3, Available values 2,3,4,6 + # All features are defined in their own files. You can find example items + # at 'exampleSite/data/features'. + # For more information take a look at the README. + +[params.testimonials] + enable = true + # All testimonials are defined in their own files. You can find example items + # at 'exampleSite/data/testimonials'. + # For more information take a look at the README. + title = "What People Say About Me?" + subtitle = "" + +[params.coachingtestimonials] + enable = false + # All testimonials are defined in their own files. You can find example items + # at 'exampleSite/data/testimonials'. + # For more information take a look at the README. + title = "Coaching Reviews " + subtitle = "" + +[params.clients] + enable = false + # All clients are defined in their own files. You can find example items + # at 'exampleSite/data/clients'. + # For more information take a look at the README. + title = "Our Clients" + subtitle = "" + +[params.recent_posts] + enable = true + title = "From our blog" + subtitle = "" + hide_summary = false + +[params.footer.recent_posts] + enable = false + +[taxonomies] + category = "categories" + tag = "tags" + author = "authors" + + + +[params.shareButtons] + size = "medium" + networks = ["linkedin", "email", "telegram"] + +[params.social.share] + facebook = false + linkedin = true + twitter = false + whatsapp = false + email = true \ No newline at end of file diff --git a/content/About Me.md b/content/About Me.md new file mode 100644 index 000000000..14716fece --- /dev/null +++ b/content/About Me.md @@ -0,0 +1,30 @@ ++++ +title = "About Me" +id = "about_me" +shareButtons = false ++++ + +I am an end-to-end Full Stack Data Developer who can set up infrastructures, design the architecture, build data pipelines, and productionalize with DevOps. I have spent six years in AWS & Amazon as a Data Architect/Engineer and have a proven record of taking multi-petabyte workloads to production while ensuring minimum operational burden. I also have hands-on experience in Streaming and Batch Analytics, including real-time applications. Having worked remotely for around 6+ years, I’ve learned to avoid the pitfalls of distributed team communication. + + +My goal is to democratize knowledge and help remove the barrier of the hype around Big Data. I write on topics such as real-time data streaming, data architecture, salary negotiation, financial literacy, and more. + + + +In my free time, I love to travel so much so that in April 2022, I finished my 12-month-long road trip across Canada. +Alright, now click on a few buttons and scroll through the pages to learn more about me and how I can help you. See you on the other side! + + +## Career Timeline +* 2022 - Present: Specialist Data Architect at Databricks +* 2015 - 2022: Data Engineer, Data Architect at Amazon/AWS +* 2014 - 2015: Data Engineer, ZS Associates +* 2012 - 2014: Business Intelligence Engineer, TCS + +## Education +* Master of Science in Big Data from Simon Fraser University, Canada +* Bachelor of Technology from GGSIPU + + + + diff --git a/content/Databricks Customer Success Stories.md b/content/Databricks Customer Success Stories.md new file mode 100644 index 000000000..2cf9d6362 --- /dev/null +++ b/content/Databricks Customer Success Stories.md @@ -0,0 +1,25 @@ ++++ +title = "Databricks Customer Stories" ++++ + +**I have worked with multiple customers on their data journey. Here are some of the public stories that I have co-authored with them.** + + +--------------------------------------- +---------------------------------------- + +## Audantic +To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. [We were able to achieve a 73% reduction in the time it took our pipeline to run as well as saving 21% on the cost of the run.](https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html) + + +Ref: https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html + +--------------------------------------- +---------------------------------------- + +## Arc Resources +[ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures](https://www.databricks.com/blog/2022/05/24/arc-uses-a-lakehouse-architecture-for-real-time-data-insights-that-optimize-drilling-performance-and-lower-carbon-emissions.html). + In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + +Ref: https://www.databricks.com/blog/2022/05/24/arc-uses-a-lakehouse-architecture-for-real-time-data-insights-that-optimize-drilling-performance-and-lower-carbon-emissions.html \ No newline at end of file diff --git a/content/blog/1-on-1-coaching.md b/content/blog/1-on-1-coaching.md new file mode 100644 index 000000000..a213388ed --- /dev/null +++ b/content/blog/1-on-1-coaching.md @@ -0,0 +1,32 @@ ++++ +title = "1 on 1 Interview Coaching" +tags = ["coaching","interviewing"] +categories = ["coaching"] +banner = "https://images.unsplash.com/photo-1455849318743-b2233052fcff?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8MSUyMG9uJTIwMSUyMEludGVydmlldyUyMENvYWNoaW5nfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=500&q=60" ++++ + +**Welcome! I am a [Super Coach](https://app.igotanoffer.com/coaching/tech/jitesh/)** . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + +
+
+## What actions can candidates take to stand out? +* Blog your learnings: They do not have to be perfect but document your understanding. +* Contribute to a open source project even in a small way; it still counts a lot. +* Create public facing material in any form. +* Create your own website and curate your material. Tip: Google Sites will let you create one for free. +* It is essential to build a public portfolio of projects relevant to your target companies (and their business domain) so that you already come across as an attractive candidate to the hiring managers and teams. + + +----------------- +## How can you book a session ? +Please have a look at all the free content. Read the blog posts, watch the videos, and read the articles. If you are still interested in working with me, you can book a session with me. + +You can book me from [Stripe]((https://book.stripe.com/aEU2al4VXdVp6FG7st)) or [Interview Query](https://www.interviewquery.com/coaching) or [I Got An Offer](https://app.igotanoffer.com/coaching/tech/jitesh/). +* [Book a 45 mins 1:1](https://book.stripe.com/aEU2al4VXdVp6FG7st) introductory video call with me focused on jobs, careers, etc. +* [Book a 1 hr 30 min](https://book.stripe.com/7sI8yJewx5oTe88000) Coaching call focused mock interviews and preparation. + + + + +--------------------------------- \ No newline at end of file diff --git a/content/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users.md b/content/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users.md new file mode 100644 index 000000000..d51e4153f --- /dev/null +++ b/content/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users.md @@ -0,0 +1,91 @@ +--- +title: Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users +description: "" +date: 2023-04-19T01:04:45.471Z +preview: "" +draft: false +tags : ["delta"] +categories : ["best practices","spark streaming"] +banner: "https://miro.medium.com/v2/resize:fit:720/format:webp/0*cz6kE2uu4BUvfTo6.jpg" +--- + +## Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + +Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. + +The below checklist is not ordered, you should aim to check off as many items as you can. + +![](https://cdn-images-1.medium.com/max/5200/0*cz6kE2uu4BUvfTo6.jpg) + +## Beginners best practices checklist for Spark Streaming: + +* [ ] Choose a trigger interval over nothing at all because it helps control storage transaction api/Listing costs. This is because some Spark jobs have a component which requires a s3/adls listing operation. If our processing is very fast think <1 sec, we will keep repeating these operations and lead to unintended costs. Example .trigger(processingTime=’5 seconds’) + +* [ ] Use ADLS Gen2 on Azure over blob storage as it’s better suited for big data analytics workloads. [Read more on the differences here.](https://medium.com/awesome-azure/azure-difference-between-azure-blob-storage-and-azure-data-lake-storage-comparison-azure-blob-vs-adls-gen2-81af5ef2a6e1) + +* [ ] Make sure the table partition strategy is chosen carefully and on low cardinality columns like date , region, country, etc. My rough rule of thumb says, if you have more than 100,000 partitions then you have over-partitioned your table. Date columns make a good partition column because they occur naturally. Example for a multinational e-commerce company which operates in 20 countries and wants to store 10 years of data. Once you partition by date & country =( 365 * 10 ) * 20 = you will end up with 73,000 partitions. + +* [ ] Name your streaming query so it is easily identifiable in the Spark UI Streaming tab. + +.option(“queryName”, “IngestFromKafka”) + + (input_stream + .select(col("eventId").alias("key"), to_json(struct(col('action'), col('time'), col('processingTime'))).alias("value")) + .writeStream + .format("kafka") + .option("kafka.bootstrap.servers", kafka_bootstrap_servers_plaintext ) + .option("kafka.security.protocol", "to_be_filled") + .option("checkpointLocation", checkpoint_location ) + .option("topic", topic) + .option("queryName", "IngestFromKafka") + .start() + ) + + + spark.readStream.format(“kinesis”).**option(“streamName”, stream_name) + ** + +* [ ] Each stream must have its own checkpoint; streams must never share checkpoints. Example, if you have 2 separate streams of different sources and data needs to be written to a single delta table. You should create 2 separate checkpoints and not share a common one. You can find an example with code [here](https://canadiandataguy.medium.com/merge-multiple-spark-streams-into-a-delta-table-44301fd549bd). + +* [ ] Don’t run multiple streams on the same driver; if such requirements are there, please benchmark it by running the streams for a few days and watch for stability over driver-related issues. Multiplexing on the same cluster is generally not recommended. + +* [ ] Partition size of data in memory should be between 100–200MB. Use Spark UI and alter maxFilesPerTrigger & maxBytesPerTrigger to achieve these partition sizes of around 100–200 MB. + +* [ ] Check if a sort merge join can be changed to Broadcast hash join. Only possible if the dataset being joined is small. The dataset being broadcasted should be around 100 MB. Increase auto-broadcast hash join threshold to 1gb if needed try a bigger instance family. + +## Advanced best practices checklist for Spark Streaming: + +* Establish a naming convention for your checkpoint: Over the course of the life of your table, you will end up having multiple checkpoints due to application upgrades, logic changes, etc. Give your checkpoint a meaningful name something which tells the following: + * Target table name + * Starting timestamp: When the checkpoint came into existence + * Example naming conventions can be : + * Generic {table_location}/_checkpoints/_{target_table_name}_starting_timestamp{_actual_timestamp[}](https://lablab.ai/event) + * If source is Delta then use startingVersion {table_location}/_checkpoints/_{target_table_name}_startingVersion{_startingVersion[}](https://lablab.ai/event) + +* See Shuffle Spill (Disk) on Spark UI to be as minimum as possible. Ideally zero only shuffle read should be there. Shuffle spill disappears from UI if it’s zero. + +* Use rocks DB , if there are stateful transformations. + +* Prefer to Azure Event Hub using it it’s Kafka connector. For Azure EventHubs, the number of cores must be == to number of partitions. With Kafka connector it is different, as it can split Kafka partition into multiple Spark partitions, and this is one of the reasons to go with Kafka protocol on EventHubs. + +* If there is state always have a watermark so it can clean itself. In case you need to have infinite state, recommend you to store that in a Delta table and zorder on necessary column so lookups are fast. + +* At big scale, think close to Trillions of records in state store. If there is a deduplicate requirement, use delta merge approach over drop duplicate to avoid state store growing very large. + +* Azure instance family choices: + * F-series for map-heavy streams — parsing, json deserialization, etc. + * Fsv2-series if doing multiple streams from the same source or need a little spill space. + * DS_v2-series for streams that join tables or do aggregations. Also for delta optimize (both bin-packing and Z-Ordering) scheduled jobs + * L series has direct attached SSD which helps Delta caching + +* Don’t set the sql.shuffle.partitions too high — ideally they should be set to be equal to the total number of worker cores. You will need to clear the check point if it is not changing. It is because checkpoint has stored this information and is using that. + +## References: + + 1. [best practices from this page](https://docs.microsoft.com/en-us/azure/databricks/structured-streaming/production) + + 2. [youtube video](https://www.youtube.com/watch?v=u2YItAN7TDg&t=1974s) with tips on how to scale at production + +### Footnote: + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website [**CanadianDataGuy.com](https://canadiandataguy.com)** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. \ No newline at end of file diff --git a/content/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python.md b/content/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python.md new file mode 100644 index 000000000..d8474de7d --- /dev/null +++ b/content/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python.md @@ -0,0 +1,117 @@ +--- +title: "A Productive Life: How to Parallelize Code Execution in Python" +description: "" +date: 2023-04-23T21:38:16.119Z +preview: "" +draft: false +tags: ["python","cost-savings", "parallelization"] +categories: [] +banner: "https://cdn-images-1.medium.com/max/9400/0*Bsp64aQg2c2IYu9R" +--- + + +# A Productive Life: How to Parallelize Code Execution in Python + +Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. + +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. We will explore 3 examples here: + +## Example 1: Asyncio Tasks / create_task() + +In asyncio, a task is a unit of work that is scheduled to run on the event loop. Tasks are created from coroutines, which are functions that are defined using the async def syntax and that can suspend their execution using the await keyword. + +To create a task, we use the asyncio.create_task() function, which takes a coroutine as its argument and returns a Task object. We can then schedule the task to run on the event loop using the await keyword. + +Here’s an example: + + import asyncio + + async def function_which_will_run_in_parallel(): + # Add what you want to do here + print('function_which_will_run_in_parallel completed') + + # Orchestrate function + async def main(): + task = asyncio.create_task(function_which_will_run_in_parallel()) + await task + + asyncio.run(main()) + +In this example, we define a simple function_which_will_run_in_parallel() that waits for one second and then prints a message. In the main() function, we create a Task object using asyncio.create_task() and pass it the function. + +We then await the completion of the task using await task. When we run the main() using asyncio.run(), the Task object is created and scheduled on the event loop, which runs the function_which_will_run_in_parallel() function asynchronously. Once the function_which_will_run_in_parallel() function is complete, the Task object is marked as done, and the program exits. + +![Photo by [Sarah Dorweiler](https://unsplash.com/@sarahdorweiler?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/9400/0*Bsp64aQg2c2IYu9R) + +## Example 2: Running Tasks in Parallel + +In asyncio, we can run tasks in parallel using the asyncio.gather() function. This function takes one or more coroutines/functions as its arguments and returns a list of their results. + +Here’s an example: + + import asyncio + + async def coroutine1(): + await asyncio.sleep(1) + return 'Coroutine 1 completed' + async def coroutine2(): + await asyncio.sleep(2) + return 'Coroutine 2 completed' + + async def main(): + results = await asyncio.gather(coroutine1(), coroutine2()) + print(results) + asyncio.run(main()) + +In this example, we define two coroutines, coroutine1() and coroutine2(), that wait for one and two seconds, respectively, before returning a message. + +In the main() coroutine function, we use the asyncio.gather() function to run the two coroutines in parallel. We pass coroutine1() and coroutine2() as its arguments and use await to wait for both coroutines to complete. + +When both coroutines are complete, the asyncio.gather() function returns a list of their results, which we print to the console. + +## Example 3: Running tasks in parallel with a loop + +In this example, we define an asynchronous coroutine function fetch() that uses the aiohttp library to download the contents of a given URL. We then define the main() coroutine function that creates a list of URLs to download and uses asyncio.create_task() to create a task for each URL. We then use asyncio.gather() to wait for all tasks to complete and return their results. + +The async with aiohttp.ClientSession() context manager is used to create a session object that can be reused across multiple requests, which can improve performance. + +When we run the main() coroutine using asyncio.run(), it concurrently downloads the web pages from the list of URLs, and prints the number of bytes downloaded from each URL. + +This is just a simple example, but it demonstrates how asyncio can be used to concurrently perform I/O-bound tasks such as web page downloads. + + import asyncio + import aiohttp + + async def fetch(session, url): + async with session.get(url) as response: + return await response.text() + + async def main(): + urls = [ + 'https://example.com', + 'https://google.com', + 'https://facebook.com', + 'https://twitter.com', + 'https://linkedin.com', + ] + + async with aiohttp.ClientSession() as session: + tasks = [asyncio.create_task(fetch(session, url)) for url in urls] + + results = await asyncio.gather(*tasks) + + for url, result in zip(urls, results): + print(f"Downloaded {len(result)} bytes from {url}") + + asyncio.run(main()) + +## Conclusion + +Asyncio is a powerful framework for writing asynchronous code in Python, and with the new features introduced in Python 3.10, it has become even easier to run tasks in parallel. + +In this blog post, we learned how to create tasks using asyncio.create_task() and how to run tasks in parallel using `asyncio.gather() + + +## Footnote: + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website [**CanadianDataGuy.com](https://canadiandataguy.com)** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. \ No newline at end of file diff --git a/content/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30%-with-graviton.md b/content/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30%-with-graviton.md new file mode 100644 index 000000000..e4c4e83c9 --- /dev/null +++ b/content/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30%-with-graviton.md @@ -0,0 +1,73 @@ +--- +title: How to Cut Your Data Processing Costs by 30% with Graviton +description: "" +date: 2023-04-23T17:29:06.599Z +preview: "" +draft: false +tags: ["graviton","cost-savings"] +categories: ["databricks","spark"] +banner: "https://cdn-images-1.medium.com/max/2000/1*DwwMNsSfkCZsXZowaIilhA.png" +--- + +# How to Cut Your Data Processing Costs by 30% with Graviton + +## What is AWS Graviton ? + +AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + +With Graviton, you can enjoy lightning-fast data processing speeds while saving money on your infrastructure costs. Plus, Graviton is compatible with all your favorite tools and applications, so you can seamlessly integrate it into your existing workflow. + +Overall, AWS Graviton offers a flexible and cost-effective alternative to traditional x86-based processors, making it a popular choice for customers who are looking to optimize their cloud computing costs without sacrificing performance or reliability. + +## Cost Savings + +If you look at the screenshot below, you will find Graviton cheaper than every other series. + +**Decipher instance name: c6g.xlarge: **C stands for compute series, 6 stands for a series number, g stands for Graviton, and xLarge means 4 vCPU. + +### **Compute Intensive (C Series)** + +c6g.xlarge is 12.5% cheaper than the next cheapest instance. + +![](https://cdn-images-1.medium.com/max/2000/1*h-t7S2KmhX5fYKp1RXijdg.png) + +### **General Purpose (M Series)** + +m6g.xlarge is ~12.2% cheaper than the next cheapest instance. + +![](https://cdn-images-1.medium.com/max/2000/1*5xRyYgQp42pORNop-IydJA.png) + +### **Memory Intensive ( R Series)** + +r6g.xlarge is ~12.5% cheaper than the next cheapest instance. + +![](https://cdn-images-1.medium.com/max/2000/1*fHxdfkY3kXB73nKobJi8Bw.png) + +## This is complicated. Help me choose ? + +Let me break down the AWS instance series into simple parts. Think about how much memory you get per core, and the price increases as the memory increases. + +* Compute intensive C series provides 2GB memory per core. + +* General purpose M series provides 4GB memory per core and is 13% more expensive than the C series. + +* Memory-intensive R series provides 8 GB memory per core and is 30% more expensive than the M series. ~48% more expensive than the C series. + +I recommend that customers start with general purpose, get a baseline runtime, and then try different series. The best way to gauge what instance family would work is to identify or categorize if the workload is compute-bound, memory-bound or network bound. + +## Launch of new Graviton 3 series in 2023 + +Here are some benefits of the new Graviton 3 series; the price is ~10% more expensive Graviton 2. However, it’s still cheaper than the M6 a instance. +> M6g ($ 0.154) < M7g ($ 0.1632) < M6a ( $0.1728 ) + +![](https://cdn-images-1.medium.com/max/2000/1*DwwMNsSfkCZsXZowaIilhA.png) +[**New Graviton3-Based General Purpose (m7g) and Memory-Optimized (r7g) Amazon EC2 Instances | Amazon…** +*We’ve come a long way since the launch of the m1.small instance in 2006, adding instances with additional memory…*aws.amazon.com](https://aws.amazon.com/blogs/aws/new-graviton3-based-general-purpose-m7g-and-memory-optimized-r7g-amazon-ec2-instances/) + +## Conclusion +> As you can see, the price saving is at least ~12%, and [AWS claims 40% better price performance](https://pages.awscloud.com/rs/112-TZM-766/images/2020_0501-CMP_Slide-Deck.pdf) due to faster processors. Thus, in reality, you should be able to save 12–40% cost savings at least. In my real-world experience, I have seen 20–30% cost savings. + +## Footnote: + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website [**CanadianDataGuy.com](https://canadiandataguy.com/)** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. + diff --git a/content/blog/2023-05-09-delta-vs-parquet-which-data-storage-format-is-best-for-your-business.md b/content/blog/2023-05-09-delta-vs-parquet-which-data-storage-format-is-best-for-your-business.md new file mode 100644 index 000000000..2145c99d9 --- /dev/null +++ b/content/blog/2023-05-09-delta-vs-parquet-which-data-storage-format-is-best-for-your-business.md @@ -0,0 +1,79 @@ +--- +title: "Delta vs Parquet: Why Delta has won the game?" +description: "" +date: 2023-05-09T15:27:37.781Z +preview: "" +draft: true +tags: ["delta","parquet"] +categories: ["spark"] +--- + +## Delta vs. Parquet: A Deep Dive into Big Data Storage Solutions + +Unlocking the intricacies of big data storage solutions is pivotal in today’s data-driven landscape. As organizations grapple with vast amounts of data, choosing between storage formats like Delta and Parquet becomes crucial. Diving deep into their technical nuances, this article highlights why Delta is emerging as the preferred choice for many. From ACID transactions to schema evolution, discover the game-changing features that set Delta apart in the competitive world of data storage. + +![Photo by [Lesly Derksen](https://unsplash.com/@lderksen?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/7880/0*rID_2tqokitny5ZC) + +## 1. Introduction to Delta and Parquet + +Parquet: An open-source columnar storage format developed under the Apache Software Foundation. It is designed to be compatible with a wide variety of data processing tools in the Hadoop ecosystem. + +* Encoding: Uses dictionary encoding, run-length encoding, and bit-packing. + +* Compression: Supports multiple codecs like Snappy, Gzip, and LZO. + +* Integration: Native integration with Hadoop, Hive, Presto, and more. + +Delta: Delta Lake is more than just a file format; it’s a storage layer that brings ACID transactions to big data workloads on top of Spark. + +* Underlying Storage: Uses Parquet for physical storage but adds a transaction log. + +* Log Structure: Maintains a transaction log to keep track of commits, ensuring data integrity. + +## 2. Technical Differences + +a. ACID Transactions: + +* Delta: Uses a combination of snapshot isolation and serializability to ensure consistency. The transaction log keeps track of every change, ensuring that operations are atomic, consistent, isolated, and durable. + +* Parquet: Doesn’t have built-in transactional capabilities. + +b. Schema Evolution: + +* Delta: Supports meta-data handling, allowing for schema changes without affecting the underlying data. This is managed through the transaction log. + +* Parquet: While it can handle schema evolution, changes might require rewriting or creating new files. + +c. Time Travel: + +* Delta: Maintains versioned data, allowing users to query a snapshot of the data at any point in time. + +* Parquet: Lacks native versioning capabilities. + +d. Storage Efficiency: + +* Delta: Implements mechanisms like Z-ordering (multi-dimensional clustering) and data skipping, optimizing both storage and query performance. + +* Parquet: Relies on columnar storage and compression for efficiency but lacks advanced optimizations like Z-ordering. + +e. Merge, Update, and Delete: + +* Delta: Supports MERGE INTO operations, making it easier to handle scenarios like change data capture (CDC). + +* Parquet: These operations typically require reading the entire dataset, making changes, and then writing it back. + +## 3. Performance Insights + +* Delta: Optimized for scenarios with frequent updates. The transaction log ensures minimal data movement, and features like Z-ordering can lead to faster query performance. + +* Parquet: While read-heavy workloads perform well with Parquet, write-heavy or update-heavy workloads might face challenges due to the lack of transactional support. + +## 4. Compatibility and Ecosystem + +* Delta: Built on top of Spark, it offers seamless integration with the Spark ecosystem. Moreover, Delta tables can be read by any tool that supports Parquet. + +* Parquet: Being a widely adopted format, it boasts broad compatibility across the Hadoop ecosystem, including tools like Hive, Impala, and Presto. + +## 5. The Verdict + +While Parquet is a robust columnar storage format that has served the big data community well, Delta brings in features that cater to the evolving needs of data engineering and data science teams. Its emphasis on transactional integrity, combined with advanced optimizations, positions Delta as a formidable player in the big data storage arena. diff --git "a/content/blog/2023-06-06-Simplifying Real-time Data Processing with Spark Streaming\342\200\231s foreachBatch with working code.md" "b/content/blog/2023-06-06-Simplifying Real-time Data Processing with Spark Streaming\342\200\231s foreachBatch with working code.md" new file mode 100644 index 000000000..4a06c9e8b --- /dev/null +++ "b/content/blog/2023-06-06-Simplifying Real-time Data Processing with Spark Streaming\342\200\231s foreachBatch with working code.md" @@ -0,0 +1,175 @@ +--- +title: "Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code" +description: "" +date: 2023-06-06T17:29:06.599Z +preview: "" +draft: false +tags: ["delta","streaming","foreachbatch"] +categories: ["databricks","spark"] +banner: "https://cdn-images-1.medium.com/max/12000/0*nt5TZ66S99XLguFB" +--- + +## Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + +Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. + +![Photo by [Andrew Schultz](https://unsplash.com/@andrewschultz?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/12000/0*nt5TZ66S99XLguFB) + +## Spark Streaming & foreachBatch + +Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. This can be very useful for applications that need to respond to changes in data in real time. + +One of the features of Spark Streaming is the foreachBatch() method. This method allows you to apply a custom function to each batch of data as it arrives. This can be useful for a variety of tasks, such as: + +* Filtering data + +* Transforming data + +* Writing data to a database + +* Sending data to an external system + +The foreachBatch() method is a powerful tool that can be used to extend the capabilities of Spark Streaming. In this blog post, we will take a closer look at how to use foreachBatch(). + +### Introducing foreachBatch: + +foreachBatch is a method provided by Spark Streaming that allows developers to apply arbitrary operations on the output of a streaming query. It acts as a bridge between the streaming world and the structured world of DataFrames and Datasets. This means that we can leverage the rich functionality of Spark's structured APIs to process real-time data efficiently. + +### The Power of foreachBatch: + +The foreachBatch operation enables developers to perform batch-like operations on streaming data. Instead of processing each individual record, which can be inefficient, foreachBatch processes the data in micro-batches, offering better performance and resource utilization. This approach also provides the flexibility to leverage the full power of Spark's DataFrames, including various transformations and aggregations, to perform complex computations on streaming data. + +### Implementing foreachBatch: + +To use foreachBatch, you need to define a function that takes two arguments: the batch identifier and the DataFrame representing the micro-batch of data. Inside this function, you can apply any transformations or computations required on the streaming data. You can use Spark's SQL, DataFrame, or Dataset APIs to manipulate the data and write the results to any external systems, such as databases or file systems. + +### Benefits of foreachBatch: + + 1. Performance: foreachBatch allows batch-like processing on streaming data, resulting in improved performance compared to processing individual records. + + 2. Flexibility: Leveraging Spark’s DataFrames and Datasets provides a wide range of transformations and aggregations to handle complex computations easily. + + 3. Scalability: Spark Streaming inherently provides scalability and fault-tolerance, and foreachBatch seamlessly integrates with these capabilities. + + 4. Ecosystem Integration: The results from foreachBatch can be easily written to external systems such as databases, file systems, or streaming analytics platforms. + +## Code & Setup + +Here’s how we can use foreachBatch to achieve this: + + ∘ Define parameters for the job + ∘ Create a Streaming source + ∘ Define custom processing logic and parameters + ∘ Create an instance of forEachBatchProcessor Class with the parameters + ∘ Orchestrate the job + ∘ Look at the output table + ∘ Clean Up + +### Define parameters for the job + + target_table_name = "for_each_batch_paramerterize" + check_point_location = f"/tmp/delta/{target_table_name}/_checkpoints/" + dedupe_colum_name ="hash" + +### Create a Streaming source + +We will create a synthetic dataset. + + generated_df = ( + spark.readStream + .format("rate") + .option("numPartitions", 4) + .option("rowsPerSecond", 1 * 1000) + .load() + .selectExpr( + "md5( CAST (value AS STRING) ) as md5" + ,"value" + ,"value%1000000 as hash" + ) + ) + +### Define custom processing logic and parameters + + class forEachBatchProcessor: + def __init__(self, dedupe_column: str, filter_criteria:str, passed_value: int): + self.dedupe_column = dedupe_column + self.filter_criteria = filter_criteria + self.passed_value = passed_value + + def print_attributes(self): + attributes = vars(self) + print( + "\n".join([f"{attr}: {value}" for attr, value in attributes.items()]) + ) + + def make_changes_using_the_micro_batch(self, microBatchOutputDF, batchId: int): + self.print_attributes() + print(f"Processing batchId: {batchId}") + + # Your processing logic using the parameter + view_name = f"updates_for_batchId_{batchId}" + microBatchOutputDF.createOrReplaceTempView(view_name) + sql_logic = f""" + SELECT + * + ,{self.passed_value} as passed_value + ,{batchId} as batch_id + FROM ( + SELECT * + ,rank() over(partition by {self.dedupe_column} order by value desc) as dedupe + FROM {view_name} + WHERE + {self.filter_criteria} + ) + WHERE + dedupe =1 + """ + print(f"Processing sql_logic: {sql_logic}") + to_be_written_df = microBatchOutputDF.sparkSession.sql(sql_logic).drop("dedupe") + to_be_written_df.write.mode("append").saveAsTable(target_table_name) + +### Create an instance of forEachBatchProcessor Class with the parameters + + + instantiateForEachBatchProcessor = forEachBatchProcessor( + dedupe_column = dedupe_colum_name, + filter_criteria = "1=1", + passed_value = 3 + ) + +### Orchestrate the job + + ( + generated_df + .writeStream + #.trigger(availableNow=True) + .trigger(processingTime='10 seconds') + .option("checkpointLocation", check_point_location) + .option("queryName", "ParameterizeForEachBatch") + .foreachBatch(instantiateForEachBatchProcessor.make_changes_using_the_micro_batch) + .start() + ) + +### Look at the output table + + display(spark.read.table(target_table_name)) + +### Clean Up + + spark.sql(f""" + DROP TABLE IF EXISTS {target_table_name} + """) + dbutils.fs.rm(check_point_location,True) + +## Conclusion: + +Apache Spark Streaming’s foreachBatch operation is a powerful tool for simplifying real-time data processing. By bridging the gap between the streaming and structured worlds, it enables developers to perform batch-like operations on streaming data efficiently. Leveraging the rich functionality of Spark's DataFrames, foreachBatch empowers users to process and analyze real-time data with ease. Whether you're performing aggregations, transformations, or writing data to external systems, foreachBatch offers a flexible and scalable solution for real-time streaming applications. + +## Footnote: + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website [**CanadianDataGuy.com](https://canadiandataguy.com/)** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. + +### [Download the code](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/Spark%20Streaming%20With%20Custom%20Parameters%20For%20Each%20Batch.py) +> I want to emphasize that my blog posts are designed to be practical resources that you can readily use in your own environments. By providing code examples with careful attention to best practices, I aim to simplify the implementation of real-time data processing solutions. I encourage you to explore the blog, copy the code snippets, and adapt them to your specific needs. With these resources, you’ll be equipped to accelerate your development process and unlock the power of Spark Streaming. Dive in, leverage the code, and start building your real-time data processing pipelines with confidence! +> Go Build! +> Canadian Data Guy! diff --git a/content/blog/2023-09-12-Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale.md b/content/blog/2023-09-12-Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale.md new file mode 100644 index 000000000..41552f0c9 --- /dev/null +++ b/content/blog/2023-09-12-Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale.md @@ -0,0 +1,47 @@ +--- +title: "Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale" +description: "" +date: 2023-09-12T17:29:06.599Z +preview: "" +draft: false +tags: ["delta"] +categories: ["databricks","spark"] +banner: "https://cdn-images-1.medium.com/max/11438/0*57KLQ0FORwBigKqQ" +--- + + +## Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + +In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? And how can we optimize it to ensure our databases aren’t just fast, but consistently reliable for 99% of our users? Join us as we demystify P99 latency and delve into strategies to fine-tune it in Databricks SQL. + +![Photo by [Håkon Sataøen](https://unsplash.com/@haakon?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/11438/0*57KLQ0FORwBigKqQ) + +## What is P99 Latency? + +The P99 latency (also known as the 99th percentile latency) for SQL queries is a metric used to measure the response time of SQL queries in a database system. It represents the latency at which 99% of the queries have a response time less than or equal to the P99 latency value, and 1% have a response time greater than the P99 latency value. +> In other words, P99 latency helps you understand the worst-case response time for most of your SQL queries. It is often used to evaluate the performance of a database system and ensure that the vast majority of queries are responding quickly, even under heavy load. + +For example, if the P99 latency for a particular SQL query is 100 milliseconds, it means that 99% of the time, that query will execute in 100 milliseconds or less. However, in 1% of cases, it may take longer than 100 milliseconds. + +### To achieve a P99 latency of 5 seconds in Databricks SQL, you can follow these steps: + + 1. Optimize the table hourly by applying a WHERE filter on the timestamp. + + 2. Use at least a Medium instance of DBSQL. Larger instances provide better query performance if queries rely on caching. Caching is done at the instance level; if you have N small instances, then you will have N copies of Cached data occupying memory. + + 3. Set spark.databricks.delta.stalenessLimit to x minutes per business requirements[. This parameter defines how many minutes of old data are acceptable.](http://spark.databricks.delta.stalenessLimit) + + 4. Ensure that the columns used in the WHERE clause are part of the first 32 columns. + + 5. Run VACUUM tables at daily or weekly cadence. + + 6. Once you have achieved your P99 latency with the above suggestions, try DBSQL Serverless and monitor P99 latency. Serverless DB Sql would give you faster startup times and more performance because of faster auto-scaling capabilities. +> If you need to power an application with minimum latency, it’s possible to pre-cache data using specific commands. However, it’s important to take caution while using these commands as misconfiguration can cause more harm than good. It’s recommended to reach out to me or your Databricks representative for the command and have the scenario validated with Databricks before implementing it. I have not included the command in the blog to avoid any mishaps. + +## Reference: + + 1. spark.databricks.delta.stalenessLimit [https://docs.databricks.com/en/delta/best-practices.html#manage-data-recency](https://docs.databricks.com/en/delta/best-practices.html#manage-data-recency) + + + +[3. https://www.youtube.com/watch?v=rJDkfRPUebw&t=629s](https://www.youtube.com/watch?v=rJDkfRPUebw&t=629s) diff --git a/content/blog/2023-09-15-Databricks SQL Dashboards Guide: Tips and Tricks to Master Them.md b/content/blog/2023-09-15-Databricks SQL Dashboards Guide: Tips and Tricks to Master Them.md new file mode 100644 index 000000000..c1dabce83 --- /dev/null +++ b/content/blog/2023-09-15-Databricks SQL Dashboards Guide: Tips and Tricks to Master Them.md @@ -0,0 +1,68 @@ +--- +title: "Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems" +description: "" +date: 2023-09-15T17:29:06.599Z +preview: "" +draft: false +tags: ["dbsql"] +categories: ["databricks","spark"] +banner: "https://cdn-images-1.medium.com/max/10000/0*lFrzWG4v-HT_emCP" +--- + +## Databricks SQL Dashboards Guide: Tips and Tricks to Master Them + +Welcome to the world of Databricks SQL Dashboards! You're in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. + +![Photo by [Romain Gal](https://unsplash.com/@wamstudio?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/10000/0*lFrzWG4v-HT_emCP) + +## 1. Getting Started with Viewing and Organizing Dashboards: + +* Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. By default, your dashboards will appear in the Home folder. Alternatively, click on “Dashboards” in the sidebar. + +* Organizing Dashboards: They are sorted in reverse chronological order, but you can reorder them by clicking column headings. Starting July 10, 2023, Databricks will migrate all SQL content to the workspace browser, so ensure your dashboards are in order! + +## 2. Tags are your friend + +* Organization: Tags allow users to categorize and organize their SQL objects, such as queries, dashboards, and visualizations. This makes finding and managing these objects easier, especially in large environments. Some good tag examples are customers, departments, projects, environments, etc. + +* Access Control: Tags can be used in conjunction with access control policies. By tagging SQL objects, administrators can define fine-grained access controls based on these tags, ensuring that only authorized users can access or modify specific resources. + +* Cost Allocation: Databricks allow you to track cloud costs associated with running queries. By tagging SQL queries or jobs, organizations can allocate costs to specific departments, projects, or teams, providing better visibility into resource consumption. + +## 3. Cloning: Replicating Success: + +* If you’ve created a dashboard you’re particularly proud of, clone it! Ensure you have the necessary permissions; thenew dashboard will list you as its owner. Note: Sharing settings, alerts, and subscriptions won’t be copied to the new dashboard. + +## 4. Harnessing the Power of Query Parameters: + +* Parameter Types: Your queries can use parameters or static values. Depending on your needs, visualizations based on parameterized queries can be set to use widget parameters, dashboard parameters, or static values. + +* Multiple Visualizations: One of the strengths of Databricks is that dashboard parameters can apply to multiple visualizations, offering a cohesive data representation. + +## 5. Editing and Customizing Your Dashboard: + +* Making Changes: Dashboards can be edited to add or remove content, edit visualizations, and apply filters. Dive in and make it truly your own! + +* Colour Customization: Stand out with unique dashboard colours. Create your own color palettes or use the default ones to make your dashboard visually appealing. + +## 6. Keeping Your Data Fresh with Refreshes: + +* Why Refresh?: Dashboards fetch data from a cache. To ensure up-to-date data representation, it’s crucial to refresh them periodically. + +* Manual vs. Automatic: Depending on your needs, dashboards can be refreshed manually or set to refresh automatically on a schedule. + +## 7. Stay Updated with Dashboard Subscriptions: + +* Snapshot Subscriptions: Subscribe to dashboard snapshots and receive periodic email updates. This is especially useful for keeping stakeholders in the loop. + +* File Size Limit: Remember there’s a 6 MB file size limit for email attachments. If your dashboard is data-heavy, subscribers will receive a link to the refreshed dashboard instead. + +## 8. Managing and Optimizing Dashboards: + +* Download, Trash, Restore: If needed, dashboards can be downloaded as PDFs, moved to Trash, or even restored. + +* Admin Privileges: If you’re a workspace admin user, you can access an admin view to oversee all dashboards and manage permissions effectively. + +* Transferring Ownership: Need to hand over a dashboard to another team member? No problem! Ownership can be easily transferred. + +In conclusion, Databricks SQL dashboards offer a versatile data visualization and analysis platform. With this step-by-step guide, you’re well on your way to mastering the art of creating, managing, and optimizing your dashboards. Dive in and explore the world of data with Databricks! diff --git a/content/blog/2023-09-29-SolvingDeltaTableConcurrencyIssuesPracticalCodeSolutionsInsights.md b/content/blog/2023-09-29-SolvingDeltaTableConcurrencyIssuesPracticalCodeSolutionsInsights.md new file mode 100644 index 000000000..8643309bf --- /dev/null +++ b/content/blog/2023-09-29-SolvingDeltaTableConcurrencyIssuesPracticalCodeSolutionsInsights.md @@ -0,0 +1,140 @@ +--- +title: "Solving Delta Table Concurrency Issues" +description: "" +date: 2023-09-30T17:29:06.599Z +preview: "" +draft: false +tags: ["delta","concurrency"] +categories: ["databricks","spark"] +banner: "https://cdn-images-1.medium.com/max/2000/0*QQtHSumIKaAxKya8" +--- + +## Solving Delta Table Concurrency Issues + +Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as `ConcurrentAppendException`, `ConcurrentDeleteReadException,` and `ConcurrentDeleteDeleteException.` In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + +![](https://cdn-images-1.medium.com/max/2000/0*QQtHSumIKaAxKya8) + +## Why Do These Issues Happen? + +* Concurrent Append Exception (`ConcurrentAppendException`):** + This error happens when another operation is adding files to the same section (or any section in a table without partitions) that your operation is reading from. These file additions can happen due to INSERT, DELETE, UPDATE, or MERGE operations. By default, with the WriteSerializable isolation level, adding files without checking any data (known as blind INSERT operations) won’t cause any issues with any operation, even if they are working on the same section (or any section in a table without partitions). However, if the isolation level is changed to Serializable, then these blind additions may cause conflicts. This error is commonly seen during simultaneous DELETE, UPDATE, or MERGE operations. Even though these operations might be updating different sections, a conflict can occur if one operation is reading the same section that another operation is updating at the same time. + +* Concurrent Delete Read Exception: +It occurs when a transaction is trying to read a file that is being deleted by another transaction. This is to ensure that a transaction does not read data that is in the process of being deleted. + +* Concurrent Delete Delete Exception: + — Occurs when two transactions are trying to delete the same file. + — Delta Lake ensures that a file is not deleted more than once. + +## Understanding Isolation Levels: Serializable vs. WriteSerializable + +Isolation levels in a database control how much transactions are protected from each other’s changes. Delta Lake on Databricks offers two such levels: Serializable and WriteSerializable. + +**1. Serializable:** + — This is the highest level of isolation. + — It ensures that all write and read operations are done in a specific order, just like how they appear in the table’s history. + — This means operations are carried out one by one, maintaining the order and ensuring the final result is as expected. + +**2. WriteSerializable (Default):** + — This level is a bit more relaxed compared to Serializable. + — It guarantees order only for write operations, not for reads. + — Even though it’s more relaxed, it’s still more strict than the Snapshot isolation level. + — This level is used by default as it offers a good mix of data consistency and availability for most operations. + +## Solution 1: Setting the Isolation Level: + +* Use the `ALTER TABLE` command to set the isolation level to `Serializable` or `WriteSerializable`. + + ALTER TABLE SET TBLPROPERTIES ('delta.isolationLevel' = 'WriteSerializable') + +## Solution 2: Avoiding Conflicts Using Partitioning and Disjoint Command Conditions + +When working with tables, sometimes two operations can clash or conflict, especially if they are working on the same set of files. This can cause problems and errors. But, there’s a way to avoid this! You can organize or partition your table based on certain columns that are often used in operations. This way, different operations work on different parts of the table, preventing them from clashing. + +For example, imagine two commands — one is updating the table for dates after January 1, 2010, and another is deleting from the table for dates before January 1, 2010. These two can clash if the table is not organized by date, as both might try to change the same files. But if you partition the table by date, these operations won’t conflict, making things smooth and error-free. + +However, be careful while choosing the column for partitioning. If you choose a column that has a lot of unique values, it can create a large number of subdirectories. This can lead to other issues, affecting the performance of operations on the table. + +By using these strategies and understanding the insights from Databricks regarding isolation levels, row-level concurrency, and write conflicts, you can make your Delta operations more robust, reliable, and efficient. + +## Solution 3: Code block with exponential retry + +The Python code below offers a robust solution to address this challenge. It is designed to manage concurrent write operations to a Delta table or path by intelligently retrying the operation in the event of specific concurrent exceptions. Streaming_write_with_concurrent_retry takes parameters such as the data stream, maximum attempts, and others to provide flexibility and control. It employs a while loop to attempt the write operation continuously and waits for its completion. In case of concurrent exceptions, it increments the attempt counter and calculates the sleep time using an exponential backoff strategy before retrying the operation. This approach ensures that the write operation is eventually successful, providing reliability and efficiency in handling concurrent operations on Delta tables. Explore the code below to understand its workings and integrate it into your projects to enhance concurrent operations handling. + + from datetime import datetime + from time import sleep + from delta.exceptions import ( + ConcurrentAppendException, + ConcurrentDeleteReadException, + ConcurrentDeleteDeleteException, + ) + import math + + + def streaming_write_with_concurrent_retry( + stream, max_attempts=3, indefinite=False, table=None, path=None + ): + """ + Handles concurrent write operations to a Delta table or path by retrying the operation + in case of specific concurrent exceptions. + + :param stream: The data stream to be written. + :param max_attempts: The maximum number of retry attempts. Default is 3. + :param indefinite: If True, will keep retrying indefinitely. Default is False. + :param table: The Delta table to write to. + :param path: The path to write to. + :return: The result of writer.awaitTermination(). + """ + + attempt = 0 # Initialize attempt counter + + while True: + try: + # Choose the writer based on whether table or path is provided + if table: + writer = stream.table(table) + elif path: + writer = stream.start(path) + else: + writer = stream.start() + + # Attempt to write and wait for termination + return writer.awaitTermination() + + # Handle concurrent exceptions + except ( + ConcurrentAppendException, + ConcurrentDeleteReadException, + ConcurrentDeleteDeleteException, + ) as e: + + # Increment attempt counter + attempt += 1 + + # If indefinite is False and attempts have reached max_attempts, raise the exception + if not indefinite and attempt >= max_attempts: + raise e from None + + # Calculate sleep time using exponential backoff strategy + sleep_time = min(120, math.pow(2, attempt)) + + # Sleep for the calculated time before retrying + sleep(sleep_time) + +### Solution 4: Row-Level Concurrency (Advanced Feature)? + +* Reduces conflicts between concurrent write operations by detecting changes at the row level. + +* Automatically resolves competing changes in concurrent writes that update or delete different rows in the same data file. +> Available only on Delta tables with deletion vectors enabled and on Photon-enabled compute running Databricks Runtime 14.0 and above. + +### Reference +[**Isolation levels and write conflicts on Databricks** +*Learn about the isolation levels and potential conflicts when performing concurrent transactions on tables on…*docs.databricks.com](https://docs.databricks.com/en/optimizations/isolation-level.html) + +## Thank You for Reading! + +I hope you found this article helpful and informative. If you enjoyed this post, please consider giving it a clap 👏 and sharing it with your network. Your support is greatly appreciated! + +— [**CanadianDataGuy](https://canadiandataguy.com/)** diff --git a/content/blog/ArcResources.md b/content/blog/ArcResources.md new file mode 100644 index 000000000..3bd9a0c16 --- /dev/null +++ b/content/blog/ArcResources.md @@ -0,0 +1,17 @@ +--- +title: "ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions" +date: 2022-05-24T17:35:21-05:00 +draft: false +tags : ["spark","delta","optimize","spark streaming"] +categories : ["customer stories"] +banner: "https://www.databricks.com/wp-content/uploads/2022/05/db-120-blog-img-1.png" +--- + +[ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures](https://www.databricks.com/blog/2022/05/24/arc-uses-a-lakehouse-architecture-for-real-time-data-insights-that-optimize-drilling-performance-and-lower-carbon-emissions.html). + In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). \ No newline at end of file diff --git a/content/blog/Audantic.md b/content/blog/Audantic.md new file mode 100644 index 000000000..b7424b406 --- /dev/null +++ b/content/blog/Audantic.md @@ -0,0 +1,19 @@ +--- +title: "How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments" +date: 2022-05-05T17:35:21-05:00 +draft: false +tags : ["delta live tables","spark","delta","optimize","spark streaming"] +categories : ["customer stories","databricks"] +banner: "https://www.databricks.com/wp-content/uploads/2022/04/db-80-blog-img-2.png" +--- + +To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. [We were able to achieve a 73% reduction in the time it took our pipeline to run as well as saving 21% on the cost of the run.](https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html) + + +https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html + + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). \ No newline at end of file diff --git a/content/blog/DatabricksWorkspaceBestPracticesAChecklistForBothBeginnersAndAdvancedUsers.md b/content/blog/DatabricksWorkspaceBestPracticesAChecklistForBothBeginnersAndAdvancedUsers.md new file mode 100644 index 000000000..a09b861f9 --- /dev/null +++ b/content/blog/DatabricksWorkspaceBestPracticesAChecklistForBothBeginnersAndAdvancedUsers.md @@ -0,0 +1,79 @@ +--- +title: "Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users" +date: 2023-02-23T17:35:21-05:00 +draft: false +tags : ["workspace"] +categories : ["best practices","databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/0*On2cbUBjrA__byVR" +--- + +## Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + +Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. + +### Here are some basic rules for using Databricks Workspace: + + 1. Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. Use a consistent naming convention and create a logical hierarchy of folders. + + 2. Use Databricks jobs for every scheduled job: If it’s a scheduled job, then it should run on a job Cluster. It’s cheaper to run on a job cluster over an interactive cluster. + + 3. [Service principals](https://docs.databricks.com/administration-guide/users-groups/service-principals.html) should own all production jobs so those permissions stay intact whenever individuals leave the team or company. Ensure the principals have access to the underlying notebooks for this to work correctly. + + 4. For jobs with multiple tasks, task values should be used so that parameters must be set at the beginning of a job. Typically, we’ve had the first task of a multi-task workflow put the parameters into task values so that all other tasks can pull accordingly as needed. Make sure to set the default and debugValues so that individual notebook-level testing can still take place. + + 5. Use Databricks CLI/API: Use Databricks CLI to automate repetitive tasks, such as uploading and downloading notebooks and managing clusters. + + 6. Use secrets management: Use Databricks secrets management to store and manage sensitive information, such as API keys and database credentials. + + 7. Optimize cluster usage: Use autoscaling to scale your clusters up or down based on workload automatically. Use autoscaling and right-sizing to optimize your cluster usage and reduce costs. + + 8. Monitor cluster performance: Monitor your cluster performance using Databricks metrics and logs to identify performance bottlenecks and optimize your workload. + + 9. Use Databricks Delta: Use Databricks Delta for data management, which provides reliability, performance, and scalability for your data. Delta helps you manage your data using ACID transactions, schema enforcement, and data versioning. + + 10. Use Databricks MLflow: Use Databricks MLflow to track and manage your machine learning experiments. MLflow helps you manage your experiments, track model performance, and deploy models to production. + + 11. A single logical top-level construct: is an E2 master account (AWS) or a subscription object (Azure Databricks/GCP). In AWS, provision a single E2 account per organization that provides a unified pane of visibility and control to all workspaces. In this way, your admin activity is centralized, with the ability to enable SSO, [Audit Logs](https://www.databricks.com/blog/2020/06/02/monitor-your-databricks-workspace-with-audit-logs.html), and [Unity Catalog](https://www.databricks.com/blog/2021/05/26/introducing-databricks-unity-catalog-fine-grained-governance-for-data-and-ai-on-the-lakehouse.html). + + 12. Adopt Unity Catalog for data governance. Enabling cross-cloud and cross-workspace analytics brings a new level of governance and control to the Lakehouse. + +![Photo by [Marco Bicca](https://unsplash.com/@mbicca?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/12032/0*On2cbUBjrA__byVR) + +## Once you have multiple teams using the same workspace, it’s time to set more controls. + +### Here are examples of some Advanced best practices to put in place: + + 1. [Cost Control by establishing boundaries on compute](https://learn.microsoft.com/en-us/azure/databricks/administration-guide/clusters/policies): Cloud can become expensive fast if no limits are set. A cluster policy is a tool that limits a user or group’s [cluster creation](https://learn.microsoft.com/en-us/azure/databricks/clusters/create-cluster) permissions based on a set of policy rules. + + 2. Tag everything: Keep an eye on your usage and know the [Databricks Resource Limits](https://docs.databricks.com/resources/limits.html); if your workspace usage or user count starts to grow, consider adopting a more involved workspace organization strategy to avoid per-workspace limits. Leverage [resource tagging](https://docs.databricks.com/administration-guide/account-settings/usage-detail-tags-aws.html) wherever possible to track cost and [usage](https://docs.databricks.com/administration-guide/account-settings-e2/usage.html) metrics. + + 3. Publish a shared central repository of code examples which are unique to your business. Example: How to connect to your on-prem SQL Server, Oracle, Salesforce, etc. + + 4. Automate your cloud processes. This ranges from every aspect of your infrastructure, including SSO/SCIM, Infrastructure-as-Code with a tool such as Terraform, CI/CD pipelines and [Repos](https://docs.databricks.com/repos.html), cloud backup, and monitoring (using both cloud-native and third-party tools). + + 5. Enable audit logging at the account level. Having auditability from the very start of your lakehouse journey allows you to establish a historical baseline. Often, you only realize how much you need audit logs when you really, really need them. + + 6. Setup [Account Level Identities](https://docs.databricks.com/data-governance/unity-catalog/manage-identities.html#account-level-identities) should be enabled as this allows for centralized principal management for all workspaces, thereby simplifying administration. We recommend setting up features like [SSO](https://docs.databricks.com/administration-guide/users-groups/single-sign-on/index.html#set-up-single-sign-on), [SCIM](https://docs.databricks.com/dev-tools/api/latest/scim/scim-account.html) and [Audit Logs](https://docs.databricks.com/administration-guide/account-settings/audit-logs.html) at the account level. [Workspace-level SSO is still required](https://docs.databricks.com/data-governance/unity-catalog/manage-identities.html#how-do-you-manage-sso-for-workspace--and-account-level-identities), until the SSO Federation feature is available. + + 7. Tracking the ongoing consumption for all workload types across all workspaces is visible to account admins via the accounts console. We recommend setting up [billable usage log delivery](https://docs.databricks.com/administration-guide/account-settings/billable-usage-delivery.html) so that it all goes to your central cloud storage for chargeback and analysis. Budget API (In Preview) should be configured at the account level, which allows account administrators to create thresholds at the workspaces, SKU, and cluster tags level and receives alerts on consumption so that timely action can be taken to remain within allotted budgets. Use a tool such as [Overwatch](https://databrickslabs.github.io/overwatch/) to track usage at an even more granular level to help identify areas of improvement when it comes to utilizing computing resources. + +### Reference: +[**5 Best Practices for Databricks Workspaces**](https://www.databricks.com/blog/2022/03/10/functional-workspace-organization-on-databricks.html) +[**Databricks Workspace Administration - Best Practices for Account, Workspace and Metastore Admins**](https://www.databricks.com/blog/2022/08/26/databricks-workspace-administration-best-practices-for-account-workspace-and-metastore-admins.html) + +[https://www.databricks.com/blog/2022/10/18/best-practices-cost-management-databricks.html](https://www.databricks.com/blog/2022/10/18/best-practices-cost-management-databricks.html) +[**Databricks Workspace Administration - Best Practices for Account, Workspace and Metastore Admins**](https://www.databricks.com/blog/2022/08/26/databricks-workspace-administration-best-practices-for-account-workspace-and-metastore-admins.html) +[**Best Practices for Cost Management on Databricks**](https://www.databricks.com/blog/2022/10/18/best-practices-cost-management-databricks.html) +[**Serving Up a Primer for Unity Catalog Onboarding**](https://www.databricks.com/blog/2022/11/22/serving-primer-unity-catalog-onboarding.html) + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). + + + + + + +{{< medium url="https://medium.com/@canadiandataguy/databricks-workspace-best-practices-a-checklist-for-both-beginners-and-advanced-users-fd7840b3d340" >}} \ No newline at end of file diff --git a/content/blog/DeltaLiveTablesAdvancedQ&A.md b/content/blog/DeltaLiveTablesAdvancedQ&A.md new file mode 100644 index 000000000..7ddaedbb1 --- /dev/null +++ b/content/blog/DeltaLiveTablesAdvancedQ&A.md @@ -0,0 +1,198 @@ +--- +title: "Delta Live Tables Advanced Q & A" +date: 2023-03-03T17:35:21-05:00 +draft: false +tags : ["delta live tables"] +categories : ["streaming","spark streaming","databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/0*fnEnj3G9nKFHpHUI" +--- + + +## Delta Live Tables Advanced Q & A + +This is primarily written for those trying to handle edge cases. + +![Photo by [Joshua Earle](https://unsplash.com/@joshuaearle?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/6000/0*fnEnj3G9nKFHpHUI) + +### Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? + +The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + +**What is the gotcha here?** + +The data has lost its natural ordering which is fine in most cases, meaning it did not go into the Delta table in the same order it was generated. This is in contrast to an ideal world in which Kafka had infinite retention, and a single DLT pipeline would have ingested the data. + +If and only if you are using the table as a Streaming source with Watermarking downstream then while reading this data, we will have to instruct Spark Streaming to sort the data while reading it. We can do this by using the following parameter ‘withEventTimeOrder’. + + spark.readStream.format("delta") + .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}") + .option("withEventTimeOrder", "true") + .table(f"{schema_name}.{table_name}") + +You can further read about this solution here [https://canadiandataguy.medium.com/how-to-write-your-first-spark-application-with-stream-stream-joins-with-working-code-dd9b0b39f814#d828](https://canadiandataguy.medium.com/how-to-write-your-first-spark-application-with-stream-stream-joins-with-working-code-dd9b0b39f814#d828) + +To reiterate, the gotcha only applies if you use this table as a Streaming Source along with Watermarking. + +### **Q2.) How do I handle deletes in a Streaming Table?** + +Let’s take GDPR as an example of where we need to enforce retention on the Delta table. One can run a regular DELETE command on the table and then in the DLT pipeline make changes to downstream consumers. +> “By default, streaming tables require append-only sources. When a streaming table uses another streaming table as a source, and the source streaming table requires updates or deletes, for example, GDPR “right to be forgotten” processing, the skipChangeCommits flag can be set on the target streaming table to ignore those changes. For more information about this flag, see [Ignore updates and deletes](https://docs.databricks.com/en/structured-streaming/delta-lake.html#ignore-changes).” + + @table + def b(): + return spark.readStream.option("skipChangeCommits", "true").table("LIVE.A") + +[https://docs.databricks.com/en/delta-live-tables/python-ref.html#configure-a-streaming-table-to-ignore-changes-in-a-source-streaming-table](https://docs.databricks.com/en/delta-live-tables/python-ref.html#configure-a-streaming-table-to-ignore-changes-in-a-source-streaming-table) + +### **Q3.) How to enable mergeSchema on DLT table?** + +This is already handled in DLT. If you want to control otherwise explicitly, you can pass the following spark conf property at the DLT pipeline or table level. +> spark.databricks.delta.schema.autoMerge.enabled True + +If you are using Autoloader, consider playing with different schema evolution modes while reading data. + +.option("cloudFiles.schemaEvolutionMode", "addNewColumns") + +### Q4.) How to change the location where the table is stored? + + 1. Manually copy the data using [Deep Clone](https://docs.databricks.com/optimizations/clone.html) with the {new_location} + + 2. Create a new DLT pipeline and set the path = {new_location} + + @dlt.table( + name="", + comment="", + spark_conf={"" : ""}, + table_properties={"" : "", "" : ""}, + path="", + partition_cols=["", ""], + schema="schema-definition", + temporary=False) + +3. In your DLT pipeline configuration, set this property pipelines.tableManagedByMultiplePipelinesCheck.enabledto false + +4. Now, we need to make sure that we do not read any duplicate data because we cannot reuse our old checkpoint. We will solve this by using filters or providing a starting configuration for the streaming source. E.g., if your streaming source is: + +**4. a) Kafka**: Then we will provide offset information. More information can be found [here](https://canadiandataguy.medium.com/how-to-upgrade-your-spark-stream-application-with-a-new-checkpoint-4dce7fa2cd96). + +**4. b) Delta:** + +For example, suppose you have a table user_events. If you want to read changes since version 5, use: + + spark.readStream.format("delta") + .option("startingVersion", "5") + .load("/tmp/delta/user_events") + +If you want to read changes since 2023–03–03, use: + + spark.readStream.format("delta") + .option("startingTimestamp", "2018-10-18") + .load("/tmp/delta/user_events") + +More details can be found [here](http://For example, suppose you have a table user_events. If you want to read changes since version 5, use: Scala Copy to clipboardCopy spark.readStream.format("delta") .option("startingVersion", "5") .load("/tmp/delta/user_events") If you want to read changes since 2018-10-18, use: Scala Copy to clipboardCopy spark.readStream.format("delta") .option("startingTimestamp", "2018-10-18") .load("/tmp/delta/user_events")). + +5. To do step 4, you should parameterize your DLT pipeline, which can be done by following these [instructions](https://medium.com/towardsdev/how-to-parameterize-delta-live-tables-and-import-reusable-functions-1994156db7fb). + +### Q5.) Does DLT support Identity Columns? + +Yes, more details [here](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-faqs-issues.html#how-do-i-use-identity-columns-when-creating-live-tables-with-sql). However, Identity columns are not supported with [APPLY CHANGES](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cdc.html) tables. + +### Q6.) How to stream out of a table which was loaded using apply_changes? + +This is generally not recommended. The target of the APPLY CHANGES INTO query or apply_changes the function cannot be used as a source for a streaming live table. A table that reads from the target of a APPLY CHANGES INTO query or apply_changes function must be a live table. + +You can rely on enabling [SCD](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cdc.html) and then use audit columns (__START_AT &__END_AT)to identify the changes. However, the downstream would still have to do a batch read and filter on these audit columns to limit the information being read. + +**If you are adventurous** and still want to do a read stream of this source. You need to enableChangeDataFeed on the delta table ‘fact_sales’. + + @dlt.table(name="fact_sales", + comment="This is a fact tables for sales", + partition_cols = ["order_date"], + table_properties={ + "pipelines.autoOptimize.zOrderCols": "StoreId,ItemId", + "delta.enableChangeDataFeed": "true", + } + ) + +Then you can decide to stream changes out of the __apply_changes_{table_name} . Make sure to handle tombstones/deletes as part of your downstream pipeline. + +### Q7.) How to delete Data using DLT? + +Use the [Change Data Capture](https://docs.databricks.com/delta-live-tables/cdc.html) functionality of DLT. The particular expression which will help you achieve this is called **apply_as_deletes. **You can change the parameter to match your custom criteria. For example, if you had bad records originating in a specific time interval or file name, you can change the expression to meet your custom criteria. + + import dlt + from pyspark.sql.functions import col, expr + + @dlt.view + def users(): + return spark.readStream.format("delta").table("cdc_data.users") + + dlt.create_streaming_live_table("target") + + dlt.apply_changes( + target = "target", + source = "users", + keys = ["userId"], + sequence_by = col("sequenceNum"), + apply_as_deletes = expr("operation = 'DELETE' or {any other custom logic} "), + except_column_list = ["operation", "sequenceNum"], + stored_as_scd_type = "2" + ) + +### Q8.) How to avoid accidental overwrites in DLT? + +Set this property so that tables cannot be overwritten. + +pipelines.reset.allowed false + +### Q9.) DLT Pipeline was deleted, but the Delta table exists. What to do now? What if the owner has left the org and I need a new DLT pipeline to take care of the table + +Step 1.) Verify via CLI if the pipeline has been deleted + + databricks --profile pipelines list + databricks --profile pipelines get --pipeline-id + +Step 2.) Change the owner of the table + + ALTER TABLE . SET TBLPROPERTIES(pipelines.pipelineId = ''); + +Note: In case you do not have a pipeline ID yet, you can use the below parameter once; run your pipeline to get the pipeline ID and then remove the below parameter. + +pipelines.tableManagedByMultiplePipelinesCheck.enabledto false + +### Q10.) How does sequence_by work in apply_changes() ? + +There are two types of data management strategies with apply_changes: + +Type 1 involves keeping only the latest state of a record. This means that if an older record arrives out-of-order and we already have a newer record in the target, the older record will not update the target because it is not the latest state. + +Type 2 involves keeping a history of all records. This means that if an out-of-order record arrives, it is considered as a historical entry and will update the table by adding a new entry to the history. + +If you experience unexpected behavior with deletes; you should consider altering the following property pipelines.cdc.tombstoneGCThresholdInSeconds +[**Delta Live Tables properties reference** +*This article provides a reference for Delta Live Tables JSON setting specification and table properties in Databricks.*docs.databricks.com](https://docs.databricks.com/en/delta-live-tables/properties.html#cdc) + +### Q11.) How to make Python UDF work in DLT + UC? + +Add a tag to your pipeline “PythonUDF.enabled”: “true” and DLT “channel”: “PREVIEW” + + { + "label": "default", + "aws_attributes": { + "instance_profile_arn": "fill_this" + }, + "custom_tags": { + "PythonUDF.enabled": "true" + }, + "autoscale": { + "min_workers": 1, + "max_workers": 5, + "mode": "ENHANCED" + } + }, + +This information is subject to change, and the parameter might not be needed in the future. + +## Footnote: + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website [**CanadianDataGuy.com](https://canadiandataguy.com/)** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. diff --git a/content/blog/FromBeginnerToProAComprehensiveGuideToUnderstandingTheSparkStreamingCheckpoint.md b/content/blog/FromBeginnerToProAComprehensiveGuideToUnderstandingTheSparkStreamingCheckpoint.md new file mode 100644 index 000000000..c0dea4e81 --- /dev/null +++ b/content/blog/FromBeginnerToProAComprehensiveGuideToUnderstandingTheSparkStreamingCheckpoint.md @@ -0,0 +1,133 @@ +--- +title: "Dive Deep into Spark Streaming Checkpoint" +description: "" +draft: false +date: 2023-03-21T06:14:44.575Z +draft: false +tags : ["checkpoint","streaming","spark streaming"] +categories : ["streaming","databricks"] +banner: "https://images.unsplash.com/photo-1574645434327-cb7970fe2e13?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80" +--- + +# From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint + + +Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. To mitigate this problem, Spark provides a mechanism called checkpointing. + +![](https://miro.medium.com/v2/resize:fit:720/0*ak1MMnA6tCrEkkNT) + +## Why do we even need a checkpoint? +Someone needs to remember what was done before or what was processed before, or what we know so far. All this information needs to be stored somewhere. The place where this is stored is called a Checkpoint. + + + + + +## How does checkpoint work? +Think of it as a 3 step process: + +1. Fetch the source metadata and write to Write Ahead Log (WAL)/Offsets +2. Fetch the source data, process it, and write to sink +3. Write state & commit information + +Checkpoints store the current offsets and state values (e.g. aggregate values) for your stream. Checkpoints are stream specific, so each should be set to its own location. + + + +> This is an advanced blog and should be read with the expectation of familiarizing and not understanding. Read this and bookmark it; once you come across a situation where you need to dig into the checkpoint, this blog will come in handy. + + + +## What is inside a checkpoint folder? +It will have 3 folders inside it and a metadata file: +* offsets: This contains the WAL information. +* commits: Once data is processed, the offset information will go inside it +* State: Only if stateful operations are involved. + + +* metadata: Metadata about the stream. This is a file + +![list of files](https://lh3.googleusercontent.com/wvwCRGZhF2PCv1K87iVQXx889xijfZQMl8ouEoycJmISg3NJZRMOSH6L_P5uyGCXSToPE4PKdoV9lv1GmfHEIcXwyw0zxZRYN9MppbABZ9oOJEoGidJIiudkeyhwNQO0l3A5PxxbUYysGn-urbN2fi8) + + + +### What is inside the Offsets file? +The easiest way to think about it is that once we start processing a micro-batch of data. We need to store an upper bound mark and a lower bound mark of the data. This mark could be called an offset. Think if you a measuring something with a scale and you need to log the reading. This reading, aka the offset, we will store in the offsets file. + +![Water Ruller](https://media.istockphoto.com/id/453511059/photo/deep-water-guage.jpg?s=612x612&w=is&k=20&c=FUjA9o3QJ-qW8DNWR7QLtjBczBm0jt82y9SbTT9eNRI=) + + + + +Different sources like Kafka, Kinesis, Delta, etc., all have different ways of defining offsets, but conceptually they are the same. + +* Kafka: you will find { "topic_name: {"partition_number": offset_number } . More information can be found in this [blog](https://medium.com/@canadiandataguy/how-to-upgrade-your-spark-stream-application-with-a-new-checkpoint-4dce7fa2cd96). + +For this blog, let's concentrate on Delta as a streaming source. + +* Reservoir ID (aka Table ID): This is your Delta Table id +* reservoirVersion is the version of the Delta table that the micro-batch(current stream execution) started with +* Index: File index of the current Delta Table version being processed. Every time you write to a Delta table, the Table version is incremented. As part of the write operation, multiple files are written. Within that Delta Table Version, the file number being processed is represented by the index. +* isStartingVersion: This is just true or false. It is true to denote a query starting rather than processing changes. + + + +![offsets](https://lh5.googleusercontent.com/vfg-MulqfyLYZg283T4SUXTnDk_69k2LLdDID3TUPw18_JhAcuMEGzhYUK1cUOCd5bksGK5baQ_jd1WdEyAL0XaScX89gcdfCboXC2qaiZShqjqaQr-VeEotykz-iEn0q-Q9z2XRXOynzHgm6760Pmo) + + + + +### Metadata +This stores the stream-id, which is generated when the stream starts and remains the same throughout the life of the checkpoint. + + + + +![metadata](https://lh5.googleusercontent.com/6e-50SDaKfOdxQZ8gzX6xbdY9pNNfvQzWlLvaTM1-DcdQfRKBRPKaMiEED3QB4AbfBpE5JfktYDMbn1EVfH7We8aIV3VO46Gq27on4TXIQNeFlmJbye7lxP99trJP46yDifB4uFnfJtceD_nyFOmdZ0) + + + + +### Commits +These files are generated only when the micro-batch succeeds. Offsets are generated at the start of the micro-batch. If the offset did not have a corresponding commit, a failure happened when processing that offset. + +In an ideal scenario, the number of commit files equals the number of offset files. However, when they are not equal, the next Spark Streaming knows where to start because it's stored in the offset file, which did not have a corresponding commit. Furthermore, watermarking information would be found here. + + + + + +![commits](https://lh3.googleusercontent.com/9AYQ33-jCAF0lOt6-BglpbM61m5u0R5L9jPdwVz-vDGGnNNIJlQulLPSdggnpMNeCX90u5p-MJfzk39rOkTR03JOSdCA9d1e_hD9AbjwTiSRseLlmnq9RaaWLQ6JrsvhCwgCase3-Kl-7PTyHfT9Pnc) + + + + +### State Store +This folder only has data in the case of Stateful Streaming, where the State is stored on disk for resiliency purposes. Thus when failures happen, the state can be recovered from here. +* State is also stored as a Delta table +* _metadata will hold the schema of the state + + + + + +![state](https://lh5.googleusercontent.com/uJUjG1bSX73eC0HJhBMTwuqgLyDBLmdrh6Ra0OR5TOg22jLbPoGt9Oxgh5qpUZj5iyyl5R6SUsLvNx6aX1kTgPiXdce3xsYe14nU6qqymboHl13lOCZ2ETUI4tat2kTXOr8_fgXkPvKXC5PbUsFGwaQ) + + + + +------------------------------ +------------------------ + + +## References +Please spare some time to look at the below to help absorb the above content further. +1. +2. https://www.databricks.com/blog/2022/12/12/streaming-production-collected-best-practices.html + + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). + diff --git a/content/blog/HowIWroteMyFirstSparkStreamingApplicationWithJoins.md b/content/blog/HowIWroteMyFirstSparkStreamingApplicationWithJoins.md new file mode 100644 index 000000000..514fe08f2 --- /dev/null +++ b/content/blog/HowIWroteMyFirstSparkStreamingApplicationWithJoins.md @@ -0,0 +1,149 @@ +--- +title: "How I wrote my first Spark Streaming Application with Joins?" +date: 2023-01-25T17:35:21-05:00 +draft: false +tags : ["streaming"] +categories : ["streaming","spark streaming", "databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/0*IUhD1QKtwgHKBdb8" +--- + + + +## How I wrote my first Spark Streaming Application with Joins with working code + +When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how [simple streaming ](https://www.databricks.com/blog/2022/07/14/using-spark-structured-streaming-to-scale-your-analytics.html)is and build their first application. + +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + +![Photo by [Ian Schneider](https://unsplash.com/@goian?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/10644/0*IUhD1QKtwgHKBdb8) + +## Scenario: + +Let’s assume we have a streaming source with data arriving all the time. We want to add more attributes from another table( Think lookup table/ dimension table). Thus we will stream the data and join with the lookup table via Stream-Batch join. The result would be written as a Delta table, which could be used downstream for analytics or streaming. + +**Imports & Parameters** + + from pyspark.sql import functions as F + from faker import Faker + import uuid + + # define schema name and where should the table be stored + schema_name = "test_streaming_joins" + schema_storage_location = "/tmp/CHOOSE_A_PERMANENT_LOCATION/" + + + # Please download this file from https://simplemaps.com/data/us-zips then download and place it at a location of your choice and then change the value for the variable below + static_table_csv_file = "/FileStore/jitesh.soni/data/us_zip_code_and_its_attributes.csv" + + # Static table specification + static_table_name = "static_zip_codes" + + + # Target Stareaming Table specification + target_table_name = "joined_datasets" + + # Recommend you to keep the checkpoint next to the Delta table so that you do have to notion about where the checkpoint is + checkpoint_location = f"{schema_storage_location}/{target_table_name}/_checkpoints/"Create Target Database + +* The below code will help create a schema/database with comments and storage locations for tables + + create_schema_sql = f""" + CREATE SCHEMA IF NOT EXISTS {schema_name} + COMMENT 'This is {schema_name} schema' + LOCATION '{schema_storage_location}' + WITH DBPROPERTIES ( Owner='Jitesh'); + """ + print(f"create_schema_sql: {create_schema_sql}") + +### **Generate Static Or a lookup Dataset** + +We will use a public dataset [source](https://simplemaps.com/data/us-zips) with attributes about a zip code. This could be any other static source or a Delta table being updated in parallel. + +**Note**: If you pick a static source and start streaming, Spark Streaming will only read it once. If you have a few updates to the static source, you will have to restart the Spark Stream so it rereads the static source. + +Meanwhile, if you have the Delta table as a source, then Spark Streaming will identify the update automatically, and nothing extra needs to be done. + + csv_df = ( + spark.read.option("header", True) + .option("inferSchema", True) + .csv(static_table_csv_file) + ) + display(csv_df) + csv_df.write.saveAsTable(f"{schema_name}.{static_table_name}") + +Next, we will Z-order the table on the key, which would be used in joins. This will help Spark Streaming do efficient joins because the Delta table is sorted by join key with statistics about which file contains which key value. + + spark.sql( + f""" + OPTIMIZE {schema_name}.{static_table_name} ZORDER BY (zip); + """ + ) + +### Generate Streaming Dataset + +We will generate a Streaming dataset using the Faker library. In the below code, we will define a few user-defined functions. + + fake = Faker() + fake_id = F.udf(lambda: str(uuid.uuid4())) + fake_firstname = F.udf(fake.first_name) + fake_lastname = F.udf(fake.last_name) + fake_email = F.udf(fake.ascii_company_email) + # fake_date = F.udf(lambda:fake.date_time_this_month().strftime("%Y-%m-%d %H:%M:%S")) + fake_address = F.udf(fake.address) + fake_zipcode = F.udf(fake.zipcode) + +Now, we will use *spark.readStream.format(“rate”) *to generate data at your desired rate. + + streaming_df = ( + spark.readStream.format("rate") + .option("numPartitions", 10) + .option("rowsPerSecond", 1 * 1000) + .load() + .withColumn("fake_id", fake_id()) + .withColumn("fake_firstname", fake_firstname()) + .withColumn("fake_lastname", fake_lastname()) + .withColumn("fake_email", fake_email()) + .withColumn("fake_address", fake_address()) + .withColumn("fake_zipcode", fake_zipcode()) + ) + + # You can uncomment the below display command to check if the code in this cell works + # display(streaming_df) + +## Stream- Static Join or Stream -Delta Join + +Structured Streaming supports joins (inner join and left join) between a streaming and a static DataFrame or a Delta Table. [However, a few types of stream-static outer Joins are not supported yet.](https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#support-matrix-for-joins-in-streaming-queries) + + lookup_delta_df = spark.read.table(static_table_name) + + + joined_streaming_df = streaming_df.join( + lookup_delta_df, + streaming_df["fake_zipcode"] == lookup_delta_df["zip"], + "left_outer", + ).drop("fake_zipcode") + # display(joined_streaming_df) + +### Orchestrate the pipeline and write Spark Stream to Delta Table + +Some Tips: + +* Give your streaming query a name. It’s good because this name will appear on Spark UI and help you monitor the stream. + +* If you are not planning to run the Stream continuously then use *trigger(availableNow=True)*. This helps process all pending data and then stops the stream automatically. + + ( + joined_streaming_df.writeStream + # .trigger(availableNow=True) + .queryName("do_a_stream_join_with_the_delta_table") + .option("checkpointLocation", checkpoint_location) + .format("delta") + .toTable(f"{schema_name}.{target_table_name}") + ) + +### [Download the code](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/spark_stream_static_join.py) + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). + diff --git a/content/blog/HowToGetTheJobIdAndRunIdForADatabricksJob.md b/content/blog/HowToGetTheJobIdAndRunIdForADatabricksJob.md new file mode 100644 index 000000000..29c32a279 --- /dev/null +++ b/content/blog/HowToGetTheJobIdAndRunIdForADatabricksJob.md @@ -0,0 +1,50 @@ +--- +title: "How to get the Job ID and Run ID for a Databricks Job" +date: 2023-02-23T17:35:21-05:00 +draft: false +tags : ["job_id", "run_id"] +categories : ["databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/format:webp/1*bPfmY6lG-OxW2l1s5szFsQ.png" +--- + +## **How to get the Job ID and Run ID for a Databricks Job with working code** + +Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed [here](https://docs.databricks.com/workflows/jobs/jobs.html#id4). + +This is a simple 2-step process: + + 1. Pass the parameter when defining the job/task + + 2. Get/Fetch and print the values + +### Step 1: Pass the parameters + +![](https://cdn-images-1.medium.com/max/4228/1*bPfmY6lG-OxW2l1s5szFsQ.png) + +### Step 2: Get/Fetch and print the values + + print(f""" + job_id: {dbutils.widgets.get('job_id')} + run_id: {dbutils.widgets.get('run_id')} + parent_run_id: {dbutils.widgets.get('parent_run_id')} + task_key: {dbutils.widgets.get('task_key')} + """) + +Next step, when you run the job; you should see an output like this + +![](https://cdn-images-1.medium.com/max/2220/1*McAX0ziUTXYc-aATZmHpgw.png) + +## Advanced & Optional: + +In case you do not have a specific attribute in mind but want to capture the whole context information instead. +> The below is code based and attributes are subject to change without notice + + dbutils.notebook.entry_point.getDbutils().notebook().getContext().toJson() + + + + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). diff --git a/content/blog/HowToParameterizeDeltaLiveTablesAndImportReusableFunctions.md b/content/blog/HowToParameterizeDeltaLiveTablesAndImportReusableFunctions.md new file mode 100644 index 000000000..2f74e5063 --- /dev/null +++ b/content/blog/HowToParameterizeDeltaLiveTablesAndImportReusableFunctions.md @@ -0,0 +1,215 @@ +--- +title: "How to parameterize Delta Live Tables and import reusable functions" +date: 2022-12-13T17:35:21-05:00 +draft: false +tags : ["delta live tables"] +categories : ["streaming","spark streaming","databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/0*-xOdeCgLJ0kOG2oG" +--- + + +## How to parameterize Delta Live Tables and import reusable functions with working code + +![Photo by [Roberto Nickson](https://unsplash.com/@rpnickson?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/10944/0*-xOdeCgLJ0kOG2oG) + +This blog will discuss passing custom parameters to a Delta Live Tables (**DLT**) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +> **Update**: As of [*December 2022,](https://docs.databricks.com/release-notes/delta-live-tables/2022/49/index.html) you can directly import files if the [reusable_functions.py file exists in the same repository ](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cookbook.html#import-python-modules-from-a-databricks-repo)by just using the import command, which is the preferred approach. However, in case these reusable_functions.py file exists outside the repository, you can take the sys.path.append() approach mentioned below.* + +Overall, this a 4-step process: + + 1. Create a *reusable_functions.py *file + + 2. Add code to receive the DLT parameters + + 3. Append the path to reusable_functions.py file and import the functions in the notebook + + 4. Create a DLT pipeline and set/pass parameters + +## 1. Create a *reusable_functions.py *file + +Create a reusable function in a Python File (not Notebook), so we can import it later. Let’s call the file ‘reusable_functions.py’ below and place it in a path. Please make sure to note the absolute path of the **folder** where this file will be placed. + + from pyspark.sql import DataFrame + from pyspark.sql.functions import current_timestamp, current_date + + def append_ingestion_columns(_df: DataFrame): + return _df.withColumn("ingestion_timestamp", current_timestamp()).withColumn( + "ingestion_date", current_date() + ) + + +## 2. Add code to receive the DLT parameters + +The below function is defined with try and except block so that it can work with Notebook as well, where we cannot pass the parameter value from the DLT pipeline + + from pyspark.sql import SparkSession + + spark = SparkSession.getActiveSession() + + + def get_parameter_or_return_default( + parameter_name: str = "pipeline.parameter_blah_blah", + default_value: str = "default_value", + ) -> str: + try: + parameter = spark.conf.get(parameter_name) + except Exception: + parameter = default_value + return parameter + +In this example, we will pass two parameters: *path_to_reusable_functions* & *parameter_abc. *Then we will use the function defined previously to get and set default values for both. + + path_to_reusable_functions = get_parameter_or_return_default( + parameter_name="pipeline.path_to_reusable_functions", + default_value="/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/", + ) + + parameter_abc = get_parameter_or_return_default( + parameter_name="pipeline.parameter_abc", default_value="random_default_value" + ) + +## 3. Append the path to reusable_functions.py file and import the functions in the notebook + + import sys + + # Add the path so functions could be imported + sys.path.append(path_to_reusable_functions) + + # Attempt the import + from reusable_functions import append_ingestion_columns + +Next step, we will define a function to return a DataFrame and the run display command to see the output of the function. This helps one test if the code works without running the DLT pipeline. + + def static_dataframe(): + df_which_we_got_back_after_running_sql = spark.sql( + f""" + SELECT + '{path_to_reusable_functions}' as path_to_reusable_functions + ,'{parameter_abc}' as parameter_abc + """ + ) + return append_ingestion_columns(df_which_we_got_back_after_running_sql) + + + display(static_dataframe()) + +At this point, you should be able to run your notebook and **validate everything works before we create a DLT pipeline**. + +Next step, we define a DLT table. + + import dlt + + + @dlt.table(name="static_table", comment="Static table") + def dlt_static_table(): + return static_dataframe() + +## 4. Create a DLT pipeline and set/pass parameters + +At this step, we can create a DLT pipeline via UI, add our custom parameters, and assign them values. + +![](https://cdn-images-1.medium.com/max/2000/0*1gT1kzPb_JkvO2Vn.png) + +The full JSON representation would look something like this, we only care about the ***configuration ***section in this JSON. + + { + "id": "d40fa97a-5b5e-4fe7-9760-b67d78a724a1", + "clusters": [ + { + "label": "default", + "policy_id": "E06216CAA0000360", + "autoscale": { + "min_workers": 1, + "max_workers": 2, + "mode": "ENHANCED" + } + }, + { + "label": "maintenance", + "policy_id": "E06216CAA0000360" + } + ], + "development": true, + "continuous": false, + "channel": "PREVIEW", + "edition": "CORE", + "photon": false, + "libraries": [ + { + "notebook": { + "path": "/Repos/jitesh.soni@databricks.com/material_for_public_consumption/notebooks/how_to_parameterize_delta_live_tables_and_import_reusable_functions" + } + } + ], + "name": "how_to_parameterize_delta_live_tables_and_import_reusable_functions", + "storage": "dbfs:/pipelines/d40fa97a-5b5e-4fe7-9760-b67d78a724a1", + "configuration": { + "pipeline.parameter_abc": "this_was_passed_from_dlt_config", + "pipeline.path_to_reusable_functions": "/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/" + }, + "target": "tmp_taget_schema" + } + +Trigger your DLT pipeline. + +If you have reached so far, you should have an end-to-end DLT pipeline working with parameter passing and imports. + +## *Update | How do you edit these parameters via API or CLI + +Below are screenshots of how to edit these parameters via CLI. The API solution would be similar. + +**Create a JSON file with the parameters:** + + { + "id": "d40fa97a-5b5e-4fe7-9760-b67d78a724a1", + "name": "how_to_parameterize_delta_live_tables_and_import_reusable_functions", + "clusters": [ + { + "label": "default", + "policy_id": "E06216CAA0000360", + "autoscale": { + "min_workers": 1, + "max_workers": 5, + "mode": "ENHANCED" + } + }, + { + "label": "maintenance", + "policy_id": "E06216CAA0000360" + } + ], + "configuration": { + "pipeline.parameter_created_from_jobs_cli": "this_was_created_from_jobs_cli", + "pipeline.parameter_abc": "this_was_passed_from_dlt_config_via_job_cli", + "pipeline.path_to_reusable_functions": "/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/" + }, + "libraries": [ + { + "notebook": { + "path": "/Repos/jitesh.soni@databricks.com/material_for_public_consumption/notebooks/how_to_parameterize_delta_live_tables_and_import_reusable_functions" + } + } + ] + } + +**Call the Datarbricks CLI to push the changes:** + +![](https://cdn-images-1.medium.com/max/3058/1*5Fdze_mGyR_ywFNFbl4iGw.png) + +**Go back to Delta Live Tables UI and the change would have gone through** + +![](https://cdn-images-1.medium.com/max/2548/1*dxGip_nh1nJyCefohlkZxg.png) + +### Download the code + +[DLT notebook](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/how_to_parameterize_delta_live_tables_and_import_reusable_functions.py) and [Reusable_function.py](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/reusable_functions.py) + +### References +[**Delta Live Tables settings**](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-configuration.html#parameterize-pipelines) + +[https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cookbook.html#import-python-modules-from-a-databricks-repo](https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cookbook.html#import-python-modules-from-a-databricks-repo) + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). diff --git a/content/blog/HowToPrepareYourselfToBeBetterAtDataInterviews.md b/content/blog/HowToPrepareYourselfToBeBetterAtDataInterviews.md new file mode 100644 index 000000000..e05257bd0 --- /dev/null +++ b/content/blog/HowToPrepareYourselfToBeBetterAtDataInterviews.md @@ -0,0 +1,71 @@ +--- +title: "How to prepare yourself to be better at Data Interviews?" +date: 2023-01-27T17:35:21-05:00 +draft: false +tags : ["books"] +categories : ["interviewing","coaching"] +banner: "https://miro.medium.com/v2/resize:fit:720/format:webp/1*HaEcA9459qFpzSyFKC8Lgw.jpeg" +--- + +## How to prepare yourself to be better at Data Interviews? + +In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. + +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them. + +## Big Data fundamentals and Data Warehousing + +* [Designing Data-Intensive Applications](https://amzn.to/3ik9Oqk) “The Big Ideas Behind Reliable, Scalable, and Maintainable Systems” + +When you read this book, do not expect to understand this after the first attempt; Just read through it. Some chapters make sense in the second or third iteration. Do not quit on the book because it’s too daunting at first. I have read this book at least five times and keep revising some chapters occasionally, improving my conceptual understanding. + +* [The Data Warehouse Toolkit](https://amzn.to/3Us4ij7): The Definitive Guide to Dimensional Modeling, 3rd Edition + +Written by “Ralph Kimball” who is considered a thought leader for Data Warehousing. The concepts you will learn in this book still hold true in 2023. + +![](https://cdn-images-1.medium.com/max/2000/1*HaEcA9459qFpzSyFKC8Lgw.jpeg) + +I encourage you to ponder how these concepts hold in the Big Data space when you read this book. As a function of time, many of us graduated directly into the Big Data space without understanding data warehousing enough. + +## Think in SQL: + +Most businesses do a SQL interview in addition to a coding interview because it is a crucial competence for data engineers, scientists or analysts. Building dependable and scalable data processing and data modelling solutions is your job, and SQL should come naturally to you. + +* People have a bad habit of running code every time they make a change. However, this often limits one's ability to think about what big blocks of code do together. A good rule of thumb is to run each SQL solution, at most twice, when practicing for the coding round. + +* Write formatted SQL with self-descriptive variable names + +* Learn about partitions, indexes, etc. + +* (Advanced) Learn about explain plan and how databases run your query + +## Practice Python/ ( Or a language of your choice) + +To practice your coding skills, you must use a whiteboard rather than just paper or integrated development environments (IDEs), which provide syntax support and standard formatting. Doing this will make you feel more at ease during the actual coding interview rounds. You ought to be knowledgeable about both simple and complicated issues. Learning the foundations of a programming language, like Python, and practicing its syntax and commands are good places to start. + +* Learn the Big O Notion so that you can write performant and testable code + +* Practice on an online platform: InterviewQuery, Leetcode, Hackerrank, etc + +Read the below books + +* [Elements of Programming Interviews in Python: The Insiders’ Guide](https://amzn.to/3F7J9Gj) + +* [Cracking the Coding Interview: 189 Programming Questions and Solutions](https://amzn.to/3GUFm0j) + +### Practice Interviewing: + +As you start applying for jobs and come close to the on-site interview stage, practicing mock interviews with peers and experienced coaches who can give you personalized feedback to improve your interview performance is beneficial. Interviewing is a skill that needs to be polished before you go to the on-site interviews. + +I recommend the candidates think about opportunity loss. Finding a good coach who can provide valuable tips can shorten your learning curve and will help you identify your blind spot. + +Example: If you land a job worth $100K a week early. Then a week represents **~$1923 of opportunity.** + +### Blind: + +[**Blind](https://www.teamblind.com/)** is an anonymous community **app** for the workplace. The tech community is very helpful and transparent. Always research the company, their interview style and compensation before your interviews. +> **“Opinions expressed are solely my own and do not express the views or opinions of my employer**.” + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). diff --git a/content/blog/HowToUpgradeYourSparkStreamApplicationWithANewCheckpoint.md b/content/blog/HowToUpgradeYourSparkStreamApplicationWithANewCheckpoint.md new file mode 100644 index 000000000..7af3ac1a6 --- /dev/null +++ b/content/blog/HowToUpgradeYourSparkStreamApplicationWithANewCheckpoint.md @@ -0,0 +1,113 @@ +--- +title: "How to upgrade your Spark Stream application with a new checkpoint!" +date: 2023-01-25T17:35:21-05:00 +draft: false +tags : ["delta live tables","checkpoint","kafka"] +categories : ["streaming","spark streaming"] +banner: "https://miro.medium.com/v2/resize:fit:720/0*42drq4qeYGri5KRH" +--- + +## How to upgrade your Spark Stream application with a new checkpoint With working code + +Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: + + 1. You are doing a code/application change where you are changing logic + + 2. Major Spark Version upgrade from Spark 2.x to Spark 3.x + + 3. The previous deployment was wrong, and you want to reprocess from a certain point + +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + +Not every scenario requires a new checkpoint. [Here is a list of things you can change without requiring a new checkpoint.](https://docs.databricks.com/structured-streaming/query-recovery.html#types-of-changes-in-structured-streaming-queries) + +This blog helps you understand how to handle a scenario where a new checkpoint is unavoidable. + +![Photo by [Patrick Tomasso](https://unsplash.com/@impatrickt?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/9184/0*42drq4qeYGri5KRH) + +## Kafka Basics: Topics, partition & offset + +**Kafka Cluster has Topics: **Topics are a way ****to organize messages. Each topic has a name that is unique across the entire Kafka cluster. Messages are sent to and read from specific topics. In other words, producers write data on a topic, and consumers read data from the topic. + +Topics have **Partitions, **and data/messages are distributed across partitions. Every message belongs to a single partition. + +Partition has messages, each with a unique sequential identifier within the partition called the **Offset.** + +**What is the takeaway here?** + +We must identify what offset has already been processed for each partition, and this information can be found inside the checkpoint. + +![](https://cdn-images-1.medium.com/max/6900/1*VQWCgcWIIgBJnzpjAaDj_A.png) + +## What information is inside the checkpoint? + +* Fetch metadata & write it to WAL(write-ahead log) in the checkpoint. **WAL**: a roll-forward journal that records transactions that have been committed but not yet applied to the main data + +* Fetch the actual data → process data with state info and then write it to the sink + +* Write the stateful information & commit to the checkpoint + +Under the checkpoint folder, there are four subfolders: + + 1. Sources (contain starting offset of Kafka) + + 2. Offsets (consist of WAL information) + + 3. Commits (after completion of the entire process, it goes to the commit) + + 4. State (only for stateful operations + 1 file of metadata) + +## How to fetch information about Offset & Partition from the Checkpoint folder? + +List the files at the checkpoint location; we are looking for the **offsets** folder. + + checkpoint_location= "/checkpoint_location/checkpoint_for_kafka_to_delta" + dbutils.fs.ls(checkpoint_location)dbutils.fs.ls(f”{checkpoint_location}/”) + +![](https://cdn-images-1.medium.com/max/4952/1*PfyMkhWGTUh5nxJHRoG5ug.png) + +Next, we will list the files under the commits folder and identify the most recent commits. + + dbutils.fs.ls(checkpoint_location) + dbutils.fs.ls(f”{checkpoint_location}/commits”) + + /checkpoint_location/checkpoint_for_kafka_to_delta/commits/0 + /checkpoint_location/checkpoint_for_kafka_to_delta/commits/1 + /checkpoint_location/checkpoint_for_kafka_to_delta/commits/2 + +Once we identify the last **commits** file number; we will open the equivalent offsets file. In this example, we can see the latest commits is “**2”.** + +Now let’s view the contents of the offsets file. + + #%fs head {FILL_THE_EXACT_PATH_OF_THE_FILE_WHICH_NEEDS_TO_BE_VIEWED} + %fs head /checkpoint_location/checkpoint_for_kafka_to_delta/offsets/2 + + {"batchWatermarkMs":0,"batchTimestampMs":1674623173851,"conf":{"spark.sql.streaming.stateStore.providerClass":"org.apache.spark.sql.execution.streaming.state.HDFSBackedStateStoreProvider","spark.sql.streaming.join.stateFormatVersion":"2","spark.sql.streaming.stateStore.compression.codec":"lz4","spark.sql.streaming.stateStore.rocksdb.formatVersion":"5","spark.sql.streaming.statefulOperator.useStrictDistribution":"true","spark.sql.streaming.flatMapGroupsWithState.stateFormatVersion":"2","spark.sql.streaming.multipleWatermarkPolicy":"min","spark.sql.streaming.aggregation.stateFormatVersion":"2","spark.sql.shuffle.partitions":"200"}} + {"topic_name_from_kafka":{"0":400000, "1":300000}} + +The information of interest is in the end. This has the topic name and offset per partition. + +*{“topic_name_from_kafka”:{“0”:400000, “1”:300000}}* + +## Now the easy part: Use Spark to start reading Kafka from a particular Offset + +Spark Streaming start[s read stream by default ](https://spark.apache.org/docs/latest/structured-streaming-kafka-integration.html#creating-a-kafka-source-for-streaming-queries)with the *latest *offset. However, it provides a parameter “startingOffsets” to select a custom starting point. + + + startingOffsets = """{"topic_name_from_kafka":{"0":400000, "1":300000}}""" + + kafka_stream = (spark.readStream + .format("kafka") + .option("kafka.bootstrap.servers", kafka_bootstrap_servers_plaintext ) + .option("subscribe", topic ) + .option("startingOffsets", startingOffsets ) + .load()) + + display(kafka_stream) + +And we are Done!!. Recommend parameterizing your code so that “startingOffsets” can be passed as a parameter. + +### Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). + diff --git a/content/blog/MergeMultipleSparkStreamsIntoADeltaTable.md b/content/blog/MergeMultipleSparkStreamsIntoADeltaTable.md new file mode 100644 index 000000000..e14a3a0dc --- /dev/null +++ b/content/blog/MergeMultipleSparkStreamsIntoADeltaTable.md @@ -0,0 +1,199 @@ +--- +title: "Merge Multiple Spark Streams Into A Delta Table" +date: 2022-10-13T04:09:03-05:00 +draft: false +tags : ["merge","optimize","z order","foreachBatch"] +categories : ["streaming","spark streaming", "databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/format:webp/1*mJcyWScvq8JuU0cg_yQN5g.png" +--- + +## Merge Multiple Spark Streams Into A Delta Table with working code + +This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. + +Overall, the process works in the following manner: + + 1. Read data from a streaming source + + 2. Use this special function [***foreachBatch](https://docs.databricks.com/structured-streaming/foreach.html). ***Using this we will call any user-defined function responsible for all the processing. + + 3. Our user-defined function runs the *Merge* and *Optimize over *the target Delta table. + +## Architecture + +![](https://cdn-images-1.medium.com/max/3040/1*mJcyWScvq8JuU0cg_yQN5g.png) + +First, we need some input data to merge. You could technically make a stream out of Kafka, Kinesis, s3, etc. + +However, for simplicity we will use .format(’rate’) to generate a stream. Feel free to alter numPartitions & rowsPerSecond . These parameters help you control how much volume of data you want to generate. In the below code, we generated 1,000 rows per second across 100 partitions. + +For the purpose of this blog, we will build 2 Spark streams one for each country Canada & USA. + +*USA’s stream* + + generated_streaming_usa_df = ( + spark.readStream + .format("rate") + .option("numPartitions", 100) + .option("rowsPerSecond", 1 * 1000) + .load() + .selectExpr( + "md5( CAST (value AS STRING) ) as md5" + ,"value" + ,"value%1000000 as hash" + ,"'USA' AS country" + ,"current_timestamp() as ingestion_timestamp" + ) + ) + + #display(generated_streaming_usa_df) + +*Canada’s Stream* + + generated_streaming_canada_df = ( + spark.readStream + .format("rate") + .option("numPartitions", 100) + .option("rowsPerSecond", 1 * 1000) + .load() + .selectExpr( + "md5( CAST (value AS STRING) ) as md5" + ,"value" + ,"value%1000000 as hash" + ,"'Canada' AS country" + ,"current_timestamp() as ingestion_timestamp" + ) + ) + + #display(generated_streaming_canada_df) + +## Parameters / Variables (Feel free to change as per your needs) + + target_table_name = "to_be_merged_into_table_partitioned_by_country" + check_point_location_for_usa_stream = f"/tmp/delta/{target_table_name}/_checkpoints/_usa/" + check_point_location_for_canada_stream = f"/tmp/delta/{target_table_name}/_checkpoints/_canada/" + join_column_name ="hash" + partition_column = "country" + +## Create an Empty Delta table so data could be merged into it + + #spark.sql(f""" DROP TABLE IF EXISTS {target_table_name};""") + ( + generated_steaming_usa_df.writeStream + .partitionBy(partition_column) + .format("delta") + .outputMode("append").trigger(once=True) + .option("checkpointLocation", check_point_location_for_usa_stream) + .toTable(target_table_name) + ) + +Check if data is populated. If you do not see any data, just run the above code snippet once more. Sometimes it takes time for the data to show up. + + display(spark.read.table(target_table_name)) + +## Now we will build the code for the user-defined function which does all the data processing, merge & Optimize + + def make_changes_using_the_micro_batch(microBatchOutputDF, batchId: int): + print(f"Processing batchId: {batchId}") + microBatchOutputDF.createOrReplaceTempView("updates") + spark_session_for_this_micro_batch = microBatchOutputDF._jdf.sparkSession() + spark_session_for_this_micro_batch.sql(f""" + SELECT * + FROM ( + select * + ,rank() over(partition by {join_column_name} order by value desc) as dedupe + from updates + ) + WHERE + dedupe =1 + """).drop("dedupe").createOrReplaceTempView("updates_which_need_to_be_merged") + spark_session_for_this_micro_batch.sql(f""" + MERGE INTO {target_table_name} target + using updates_which_need_to_be_merged u + on u.{partition_column} = target.{partition_column} + AND u.{join_column_name} = target.{join_column_name} + WHEN MATCHED THEN UPDATE SET * + WHEN NOT MATCHED THEN INSERT * + """) + optimize_every_n_batches = 20 + #Define how often should optimize run? for example: at 50, it means that we will run the optimize command every 50 batches of stream data + if batchId % optimize_every_n_batches == 0: + optimize_and_zorder_table(table_name = target_table_name, zorder_by_col_name = join_column_name) + +## Optimize/ Z-order a Delta table + +Why do we need to optimize a table? If we keep adding files to our Delta table and never optimize/sort them then over time we need to read a lot of files during merge time. Thus, optimizing the Delta table after every N merges is better. N needs to be decided on your latency requirements. You could start with N as 10 and change it as per your needs. + +The below code will run an optimize and zorder command on a given table that is being fed by a stream. Optimize commands can’t run in a silo because it will require us to pause and then resume the stream. Therefore, we need to call this function a part of the upsert function. This enables us to optimize before the next batch of streaming data comes through. + + from timeit import default_timer as timer + + + def optimize_and_zorder_table(table_name: str, zorder_by_col_name: str) -> None: + """ + Parameters: + table_name: str + name of the table to be optimized + zorder_by_col_name: str + comma separated list of columns to zorder by. example "col_a, col_b, col_c" + """ + start = timer() + print(f"Met condition to optimize table {table_name}") + sql_query_optimize = f"OPTIMIZE {table_name} ZORDER BY ({zorder_by_col_name})" + spark.sql(sql_query_optimize) + end = timer() + time_elapsed_seconds = end - start + print( + f"Successfully optimized table {table_name} . Total time elapsed: {time_elapsed_seconds} seconds" + ) + +## Orchestrate the streaming pipeline end to end + +Read the **Canada** stream and merge into Delta table + + ( + generated_steaming_canada_df + .writeStream.format('delta') + #.trigger(availableNow=True) + .trigger(processingTime='10 seconds') + .option("checkpointLocation", check_point_location_for_canada_stream) + .foreachBatch(make_changes_using_the_micro_batch) + .start() + ) + +Read the **USA** stream and merge into Delta table + + ( + generated_steaming_usa_df + .writeStream.format('delta') + .trigger(processingTime='10 seconds') + .option("checkpointLocation", check_point_location_for_usa_stream) + .foreachBatch(make_changes_using_the_micro_batch) + .start() + ) + +**Now, let’s validate that data is being populated** + + display( + spark.sql(f""" + SELECT + {partition_column} as partition_column + ,count(1) as row_count + FROM + {target_table_name} + GROUP BY + {partition_column} + """) + ) + +If you have reached so far, you should have an end-to-end pipeline working with streaming data and merging data into a Delta table. + +## Download this notebook + +[https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/Merge%20Multiple%20Spark%20Streams%20Into%20A%20Delta%20Table.py](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/Merge%20Multiple%20Spark%20Streams%20Into%20A%20Delta%20Table.py) + + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). \ No newline at end of file diff --git a/content/blog/UsingSparkStreamingToMergeUpsertDataIntoADeltaLakeWithWorkingCode.md b/content/blog/UsingSparkStreamingToMergeUpsertDataIntoADeltaLakeWithWorkingCode.md new file mode 100644 index 000000000..5b33bff1f --- /dev/null +++ b/content/blog/UsingSparkStreamingToMergeUpsertDataIntoADeltaLakeWithWorkingCode.md @@ -0,0 +1,151 @@ +--- +title: "Using Spark Streaming to merge/upsert data into a Delta Lake with working code" +date: 2022-10-12T04:06:14-05:00 +draft: false +tags : ["merge","optimize","z order","foreachBatch","kafka"] +categories : ["streaming","spark streaming" ,"databricks"] +banner: "https://miro.medium.com/v2/resize:fit:720/format:webp/0*7Vzrx4tKynxXNKNg.jpg" +--- + +## Using Spark Streaming to merge/upsert data into a Delta Lake with working code + +![](https://cdn-images-1.medium.com/max/2000/0*7Vzrx4tKynxXNKNg.jpg) + +This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. + +Overall, the process works in the following manner, we read data from a streaming source and use this special function [***foreachBatch](https://docs.databricks.com/structured-streaming/foreach.html). ***Using this we will call any user-defined function responsible for all the processing. This function encapsulates the *Merge* and *Optimize *to the target Delta table. + +First, we need some input data to merge. You could technically make a stream out of Kafka, Kinesis, s3, etc. for simplicity. Let’s generate a stream using the below. Feel free to alter numPartitions & rowsPerSecond . These parameters help you control how much volume of data you want to generate. In the below code, we generated 10,000 rows per second across 100 partitions. + +## Generate streaming data at your desired rate + + generated_df = ( + spark.readStream + .format("rate") + .option("numPartitions", 100) + .option("rowsPerSecond", 10 * 1000) + .load() + .selectExpr( + "md5( CAST (value AS STRING) ) as md5" + ,"value" + ,"value%1000000 as hash" + ) + ) + + #display(generated_df) + +## Parameters / Variables (Feel free to change as per your needs) + + target_table_name = "to_be_merged_into_table" + check_point_location = f"/tmp/delta/{target_table_name}/_checkpoints/" + join_column_name ="hash" + +## Create an Empty Delta table so data could be merged into it + + spark.sql(f""" DROP TABLE IF EXISTS {target_table_name};""") + ( + generated_df.writeStream + .format("delta") + .outputMode("append").trigger(once=True) + .option("checkpointLocation", check_point_location) + .toTable(target_table_name) + ) + +Check if data is populated + + display(spark.read.table(target_table_name)) + +## A user-defined function which does the data processing, Merge & Optimize + + def make_changes_using_the_micro_batch(microBatchOutputDF, batchId: int): + print(f"Processing batchId: {batchId}") + microBatchOutputDF.createOrReplaceTempView("updates") + spark_session_for_this_micro_batch = microBatchOutputDF._jdf.sparkSession() + spark_session_for_this_micro_batch.sql(f""" + SELECT * + FROM ( + select * + ,rank() over(partition by {join_column_name} order by value desc) as dedupe + from updates + ) + WHERE + dedupe =1 + """).drop("dedupe").createOrReplaceTempView("updates_which_need_to_be_merged") + spark_session_for_this_micro_batch.sql(f""" + MERGE INTO {target_table_name} target + using updates_which_need_to_be_merged u + on u.{join_column_name} = target.{join_column_name} + WHEN MATCHED THEN UPDATE SET * + WHEN NOT MATCHED THEN INSERT * + """) + optimize_every_n_batches = 20 + #Define how often should optimize run? for example: at 50, it means that we will run the optimize command every 50 batches of stream data + if batchId % optimize_every_n_batches == 0: + optimize_and_zorder_table(table_name = target_table_name, zorder_by_col_name = join_column_name) + +## Optimize/ Z-order a Delta table + +Why do we need to optimize a table? If we keep adding files to our Delta table and never optimize/sort them then over time we need to read a lot of files during merge time. Thus, optimizing the Delta table after every N merges is better. N needs to be decided on your latency requirements. You could start with N as 10 and change it as per your needs. + +The below code will run an optimize and zorder command on a given table that is being fed by a stream. Optimize commands can’t run in a silo because it will require us to pause and then resume the stream. Therefore, we need to call this function a part of the upsert function. This enables us to optimize before the next batch of streaming data comes through. + + from timeit import default_timer as timer + + + def optimize_and_zorder_table(table_name: str, zorder_by_col_name: str) -> None: + """ + Parameters: + table_name: str + name of the table to be optimized + zorder_by_col_name: str + comma separated list of columns to zorder by. example "col_a, col_b, col_c" + """ + start = timer() + print(f"Met condition to optimize table {table_name}") + sql_query_optimize = f"OPTIMIZE {table_name} ZORDER BY ({zorder_by_col_name})" + spark.sql(sql_query_optimize) + end = timer() + time_elapsed_seconds = end - start + print( + f"Successfully optimized table {table_name} . Total time elapsed: {time_elapsed_seconds} seconds" + ) + +## Orchestrate from readStream -> Merge -> Optimize + + ( + generated_df + .writeStream.format('delta') + .trigger(processingTime='30 seconds') + .option("checkpointLocation", check_point_location) + .foreachBatch(make_changes_using_the_micro_batch) + .start() + ) + +If you have reached so far, you should have an end-to-end pipeline working with streaming data and merging data into a Delta table. + +As the next step, let’s use the previous target table as our new streaming source. + +## Use the target table as a source for the next streaming pipeline + +Change data feed allows Databricks to track row-level changes between versions of a Delta table. When enabled on a Delta table, the runtime records change events for all the data written into the table. This includes the row data along with metadata indicating whether the specified row was inserted, deleted, or updated. + +Reference: [https://docs.databricks.com/delta/delta-change-data-feed.html#use-delta-lake-change-data-feed-on-databricks](https://docs.databricks.com/delta/delta-change-data-feed.html#use-delta-lake-change-data-feed-on-databricks) + + spark.sql(f''' + ALTER TABLE {target_table_name} SET TBLPROPERTIES (delta.enableChangeDataFeed=true) + ''') + +## [Reading change data as a stream](https://docs.databricks.com/delta/delta-change-data-feed.html#read-changes-in-streaming-queries) + + display( + spark.readStream.format("delta") + .option("readChangeFeed", "true") + .table(target_table_name) + ) + +### Download this notebook +[**Spark Streaming Using For Each Batch & Merge.html**](https://drive.google.com/file/d/1MWlHqy20j3g67uZhOLrjTDw1T8GioJZt/view?usp=sharing) + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs](https://canadiandataguy.com/). \ No newline at end of file diff --git a/content/blog/footnotes.md b/content/blog/footnotes.md new file mode 100644 index 000000000..a0bcbb101 --- /dev/null +++ b/content/blog/footnotes.md @@ -0,0 +1,9 @@ +--- +title: "Footnotes" +date: 2022-10-12T04:06:14-05:00 +draft: true +--- + +## Footnotes + +Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website CanadianDataGuy.com for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work. \ No newline at end of file diff --git a/content/blog/spark-stream-stream-join.md b/content/blog/spark-stream-stream-join.md new file mode 100644 index 000000000..9ce56fa34 --- /dev/null +++ b/content/blog/spark-stream-stream-join.md @@ -0,0 +1,518 @@ +--- +title: How to write your first Spark application with Stream-Stream Joins with working code. +description: "Have you been waiting to try Streaming Joins but have been unable to take the plunge? +In a single blog, we will teach you whatever needs to be understood, along with an end-to-end pipeline, which you can copy-paste and make your own." +date: 2023-03-23T06:32:42.122Z +preview: "Have you been waiting to try Streaming Joins but have been unable to take the plunge? + +In a single blog, we will teach you whatever needs to be understood, along with an end-to-end pipeline, which you can copy-paste and make your own." +draft: false +tags : ["checkpoint","streaming","spark streaming"] +categories : ["streaming","databricks"] +banner: "https://cdn-images-1.medium.com/max/2560/1*eds6wrWUDMfdL9I48-heFQ.jpeg" +--- + +## How to write your first Spark application with Stream-Stream Joins with working code. + +Have you been waiting to try Streaming but cannot take the plunge? + +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. + +The steps involved: + + 1. Create a fake dataset at scale + 2. Set a baseline using traditional SQL + 3. Define Temporary Streaming Views + 4. Inner Joins with optional Watermarking + 5. Left Joins with Watermarking + 6. The cold start edge case: withEventTimeOrder + 7. Cleanup + +![](https://cdn-images-1.medium.com/max/2560/1*eds6wrWUDMfdL9I48-heFQ.jpeg) + +## What is Stream-Stream Join? + +Stream-stream join is a widely used operation in stream processing where two or more data streams are joined based on some common attributes or keys. It is essential in several use cases, such as real-time analytics, fraud detection, and IoT data processing. + +### Concept of Stream-Stream Join + +Stream-stream join combines two or more streams based on a common attribute or key. The join operation is performed on an ongoing basis, with each new data item from the stream triggering a join operation. In stream-stream join, each data item in the stream is treated as an event, and it is matched with the corresponding event from the other stream based on matching criteria. This matching criterion could be a common attribute or key in both streams. + +When it comes to joining data streams, there are a few key challenges that must be addressed to ensure successful results. One of the biggest hurdles is the fact that, at any given moment, neither stream has a complete view of the dataset. This can make it difficult to find matches between inputs and generate accurate join results. + +To overcome this challenge, it’s important to buffer past input as a streaming state for both input streams. This allows for every future input to be matched with past input, which can help to generate more accurate join results. Additionally, this buffering process can help to automatically handle late or out-of-order data, which can be common in streaming environments. + +To further optimize the join process, it’s also important to use watermarks to limit the state. This can help to ensure that only the most relevant data is being used to generate join results, which can help to improve accuracy and reduce processing times. + +### Types of Stream-Stream Join + +Depending on the nature of the join and the matching criteria, there are several types of stream-stream join operations. Some of the popular types of stream-stream join are: + +**Inner Join** +In inner join, only those events are returned where there is a match in both the input streams. This type of join is useful when combining the data from two streams with a common key or attribute. + +**Outer Join** +In outer join, all events from both the input streams are included in the joined stream, whether or not there is a match between them. This type of join is useful when we need to combine data from two streams, and there may be missing or incomplete data in either stream. + +**Left Join** +In left join, all events from the left input stream are included in the joined stream, and only the matching events from the right input stream are included. This type of join is useful when we need to combine data from two streams and keep all the data from the left stream, even if there is no matching data in the right stream. + +## 1. The Setup: Create a fake dataset at scale + +Most people do not have 2 streams just hanging around for one to experiment with Stream Steam Joins. Thus I used Faker to mock 2 different streams which we will use for this example. + +The name of the library being used is Faker and faker_vehicle to create Datasets. + + !pip install faker_vehicle + !pip install faker + +Imports + + from faker import Faker + from faker_vehicle import VehicleProvider + from pyspark.sql import functions as F + import uuid + from utils import logger + +Parameters + + # define schema name and where should the table be stored + schema_name = “test_streaming_joins” + schema_storage_location = “/tmp/CHOOSE_A_PERMANENT_LOCATION/” + +**Create the Target Schema/Database** +Create a Schema and set location. This way, all tables would inherit the base location. + + create_schema_sql = f””” + CREATE SCHEMA IF NOT EXISTS {schema_name} + COMMENT ‘This is {schema_name} schema’ + LOCATION ‘{schema_storage_location}’ + WITH DBPROPERTIES ( Owner=’Jitesh’); + “”” + print(f”create_schema_sql: {create_schema_sql}”) + spark.sql(create_schema_sql) + +Use Faker to define functions to help generate fake column values + + fake = Faker() + fake.add_provider(VehicleProvider) + + event_id = F.udf(lambda: str(uuid.uuid4())) + vehicle_year_make_model = F.udf(fake.vehicle_year_make_model) + vehicle_year_make_model_cat = F.udf(fake.vehicle_year_make_model_cat) + vehicle_make_model = F.udf(fake.vehicle_make_model) + vehicle_make = F.udf(fake.vehicle_make) + vehicle_model = F.udf(fake.vehicle_model) + vehicle_year = F.udf(fake.vehicle_year) + vehicle_category = F.udf(fake.vehicle_category) + vehicle_object = F.udf(fake.vehicle_object) + + latitude = F.udf(fake.latitude) + longitude = F.udf(fake.longitude) + location_on_land = F.udf(fake.location_on_land) + local_latlng = F.udf(fake.local_latlng) + zipcode = F.udf(fake.zipcode) + +Generate Streaming source data at your desired rate + + def generated_vehicle_and_geo_df (rowsPerSecond:int , numPartitions :int ): + return ( + spark.readStream.format("rate") + .option("numPartitions", numPartitions) + .option("rowsPerSecond", rowsPerSecond) + .load() + .withColumn("event_id", event_id()) + .withColumn("vehicle_year_make_model", vehicle_year_make_model()) + .withColumn("vehicle_year_make_model_cat", vehicle_year_make_model_cat()) + .withColumn("vehicle_make_model", vehicle_make_model()) + .withColumn("vehicle_make", vehicle_make()) + .withColumn("vehicle_year", vehicle_year()) + .withColumn("vehicle_category", vehicle_category()) + .withColumn("vehicle_object", vehicle_object()) + .withColumn("latitude", latitude()) + .withColumn("longitude", longitude()) + .withColumn("location_on_land", location_on_land()) + .withColumn("local_latlng", local_latlng()) + .withColumn("zipcode", zipcode()) + ) + + # You can uncomment the below display command to check if the code in this cell works + #display(generated_vehicle_and_geo_df) + + # You can uncomment the below display command to check if the code in this cell works + #display(generated_vehicle_and_geo_df) + +Now let's generate the base source table and let’s call it Vehicle_Geo + + table_name_vehicle_geo= "vehicle_geo" + def stream_write_to_vehicle_geo_table(rowsPerSecond: int = 1000, numPartitions: int = 10): + + ( + generated_vehicle_and_geo_df(rowsPerSecond, numPartitions) + .writeStream + .queryName(f"write_to_delta_table: {table_name_vehicle_geo}") + .option("checkpointLocation", f"{schema_storage_location}/{table_name_vehicle_geo}/_checkpoint") + .format("delta") + .toTable(f"{schema_name}.{table_name_vehicle_geo}") + ) + stream_write_to_vehicle_geo_table(rowsPerSecond = 1000, numPartitions = 10) + +Let the above code run for a few iterations, and you can play with rowsPerSecond and numPartitions to control how much data you would like to generate. Once you have generated enough data, kill the above stream and get a base line for row count. + + spark.read.table(f"{schema_name}.{table_name_vehicle_geo}").count() + + display( + spark.sql(f""" + SELECT * + FROM {schema_name}.{table_name_vehicle_geo} + """) + ) + +Let’s also get a min & max of the timestamp column as we would be leveraging it for watermarking. + + display( + spark.sql(f""" + SELECT + min(timestamp) + ,max(timestamp) + ,current_timestamp() + FROM {schema_name}.{table_name_vehicle_geo} + """) + ) + +### Next, we will break this Delta table into 2 different tables + +Because for Stream-Stream Joins we need 2 different streams. We will use Delta To Delta Streaming here to create these tables. + + 1. **a ) Table: Vehicle** +``` +table_name_vehicle = "vehicle" +vehicle_df = ( + spark.readStream.format("delta") + .option("maxFilesPerTrigger", "100") + .table(f"{schema_name}.vehicle_geo") + .selectExpr( + "event_id", + "timestamp as vehicle_timestamp", + "vehicle_year_make_model", + "vehicle_year_make_model_cat", + "vehicle_make_model", + "vehicle_make", + "vehicle_year", + "vehicle_category", + "vehicle_object", + ) +) + + +def stream_write_to_vehicle_table(): + + ( + vehicle_df.writeStream + # .trigger(availableNow=True) + .queryName(f"write_to_delta_table: {table_name_vehicle}") + .option( + "checkpointLocation", + f"{schema_storage_location}/{table_name_vehicle}/_checkpoint", + ) + .format("delta") + .toTable(f"{schema_name}.{table_name_vehicle}") + ) + + +stream_write_to_vehicle_table() + +``` + + 1. **b) Table: Geo** + +We have added a filter when we write to this table. This would be useful when we emulate the left join scenario. Filter: where("value like '1%' ") + + geo_df = ( + spark.readStream.format("delta").option("maxFilesPerTrigger","100").table(f"{schema_name}.vehicle_geo") + .selectExpr( + "event_id" + ,"value" + ,"timestamp as geo_timestamp" + ,"latitude" + ,"longitude" + ,"location_on_land" + ,"local_latlng" + ,"cast( zipcode as integer) as zipcode" + ).where("value like '1%' ") + ) + #geo_df.printSchema() + #display(geo_df) + + table_name_geo = "geo" + def stream_write_to_geo_table(): + + ( geo_df + .writeStream + #.trigger(availableNow=True) + .queryName(f"write_to_delta_table: {table_name_geo}") + .option("checkpointLocation", f"{schema_storage_location}/{table_name_geo}/_checkpoint") + .format("delta") + .toTable(f"{schema_name}.{table_name_geo}") + ) + + stream_write_to_geo_table() + +## 2. Set a baseline using traditional SQL + +Before we do the actual streaming joins. Let’s do a regular join and figure out the expected row count. + +**Get row count from Inner Join** + + sql_query_batch_inner_join = f''' + SELECT count(vehicle.event_id) as row_count_for_inner_join + FROM {schema_name}.{table_name_vehicle} vehicle + JOIN {schema_name}.{table_name_geo} geo + ON vehicle.event_id = geo.event_id + AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 MINUTES + ''' + print(f''' Run SQL Query: + {sql_query_batch_inner_join} + ''') + display( spark.sql(sql_query_batch_inner_join) ) + +**Get row count from Inner Join** + + sql_query_batch_left_join = f''' + SELECT count(vehicle.event_id) as row_count_for_left_join + FROM {schema_name}.{table_name_vehicle} vehicle + LEFT JOIN {schema_name}.{table_name_geo} geo + ON vehicle.event_id = geo.event_id + -- Assume there is a business logic that timestamp cannot be more than 15 minutes off + AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 MINUTES + ''' + print(f''' Run SQL Query: + {sql_query_batch_left_join} + ''') + display( spark.sql(sql_query_batch_left_join) ) + +## Summary so far: + + 1. We created a Source Delta Table: vehicle_geo + + 2. We took the previous table and divided its column into two tables: Vehicle and Geo + + 3. Vehicle row count matches with vehicle_geo, and it has a subset of those columns + + 4. The Geo row count is lesser than Vehicle because we added a filter when we wrote to the Geo table + + 5. We ran 2 SQL to identify what the row count should be after we do stream-stream join + +## 3. Define Temporary Streaming Views + +Some people prefer to write the logic in SQL. Thus, we are creating streaming views which could be manipulated with SQL. The below code block will help create a view and set a watermark on the stream. + + def stream_from_delta_and_create_view (schema_name: str, table_name:str, column_to_watermark_on:str, how_late_can_the_data_be: str = "2 minutes" , maxFilesPerTrigger: int = 100): + view_name = f"_streaming_vw_{schema_name}_{table_name}" + print(f"Table {schema_name}.{table_name} is now streaming under a temporoary view called {view_name}") + ( + spark.readStream.format("delta") + .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}") + .option("withEventTimeOrder", "true") + .table(f"{schema_name}.{table_name}") + .withWatermark(f"{column_to_watermark_on}",how_late_can_the_data_be) + .createOrReplaceTempView(view_name) + ) + + +**3. a Create Vehicle Stream** + +Let’s create a Vehicle Stream and set its watermark as 1mins + + stream_from_delta_and_create_view(schema_name =schema_name, table_name = 'vehicle', column_to_watermark_on ="vehicle_timestamp", how_late_can_the_data_be = "1 minutes" ) + +Let’s visualize the stream. + + display( + spark.sql(f''' + SELECT * + FROM _streaming_vw_test_streaming_joins_vehicle + ''') + ) + +You can also do an aggregation on the stream. It’s out of the scope of this blog, but I wanted to show you how you can do it + + display( + spark.sql(f''' + SELECT + vehicle_make + ,count(1) as row_count + FROM _streaming_vw_test_streaming_joins_vehicle + GROUP BY vehicle_make + ORDER BY vehicle_make + ''') + ) + +**3. b Create Geo Stream** + +Let’s create a Geo Stream and set its watermark as 2 mins + + stream_from_delta_and_create_view(schema_name =schema_name, table_name = 'geo', column_to_watermark_on ="geo_timestamp", how_late_can_the_data_be = "2 minutes" ) + +Have a look at what the data looks like + + display( + spark.sql(f''' + SELECT * + FROM _streaming_vw_test_streaming_joins_geo + ''') + ) + +## 4. Inner Joins with optional Watermarking + +While inner joins on any kind of columns and with any kind of conditions are possible in streaming environments, it’s important to be aware of the potential for unbounded state growth. As new input arrives, it can potentially match with any input from the past, leading to a rapidly increasing streaming state size. + +To avoid this issue, it’s essential to define additional join conditions that prevent indefinitely old inputs from matching with future inputs. By doing so, it’s possible to clear old inputs from the state, which can help to prevent unbounded state growth and ensure more efficient processing. + +There are a variety of techniques that can be used to define these additional join conditions. For example, you might limit the scope of the join by only matching on a subset of columns, or you might set a time-based constraint that prevents old inputs from being considered after a certain period of time has elapsed. + +Ultimately, the key to managing streaming state size and ensuring efficient join processing is to consider the unique requirements of your specific use case carefully and to leverage the right techniques and tools to optimize your join conditions accordingly. **Although watermarking could be optional, I would highly recommend you set a watermark on both streams.** + + sql_for_stream_stream_inner_join = f""" + SELECT + vehicle.* + ,geo.latitude + ,geo.longitude + ,geo.zipcode + FROM _streaming_vw_test_streaming_joins_vehicle vehicle + JOIN _streaming_vw_test_streaming_joins_geo geo + ON vehicle.event_id = geo.event_id + -- Assume there is a business logic that timestamp cannot be more than X minutes off + AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 minutes + """ + #display(spark.sql(sql_for_stream_stream_inner_join)) + + table_name_stream_stream_innner_join ='stream_stream_innner_join' + + ( spark.sql(sql_for_inner_join) + .writeStream + #.trigger(availableNow=True) + .queryName(f"write_to_delta_table: {table_name_stream_stream_innner_join}") + .option("checkpointLocation", f"{schema_storage_location}/{table_name_stream_stream_innner_join}/_checkpoint") + .format("delta") + .toTable(f"{schema_name}.{table_name_stream_stream_innner_join}") + ) + +If the stream has finished then in the next step. You should find that the row count should match up with the regular batch SQL Job + + spark.read.table(f"{schema_name}.{table_name_stream_stream_innner_join}").count() + +### How was the watermark computed in this scenario? + +When we defined streaming views for Vehicle and Geo, we set them as 1 min and 2 min, respectively. + +If you look at the join condition we mentioned : + + AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 minutes + +5 min + 2 min = 7 min. + +Spark Streaming would automatically calculate this 7 min number and the state would be cleared after that. + +## 5. Left Joins with Watermarking + +While the watermark + event-time constraints is optional for inner joins, for outer joins they must be specified. This is because for generating the NULL results in outer join, the engine must know when an input row is not going to match with anything in future. Hence, the watermark + event-time constraints must be specified for generating correct results. + +### 5.a How Left Joins works differently than an Inner Join + +One important factor is that the outer NULL results will be generated with a delay that depends on the specified watermark delay and the time range condition. This delay is necessary to ensure that there were no matches, and that there will be no matches in the future. + +In the current implementation of the micro-batch engine, watermarks are advanced at the end of each micro-batch, and the next micro-batch uses the updated watermark to clean up the state and output outer results. However, this means that the generation of outer results may be delayed if there is no new data being received in the stream. If either of the two input streams being joined does not receive data for a while, the outer output (in both left and right cases) may be delayed. + + sql_for_stream_stream_left_join = f""" + SELECT + vehicle.* + ,geo.latitude + ,geo.longitude + ,geo.zipcode + FROM _streaming_vw_test_streaming_joins_vehicle vehicle + LEFT JOIN _streaming_vw_test_streaming_joins_geo geo + ON vehicle.event_id = geo.event_id + AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 MINUTES + """ + #display(spark.sql(sql_for_stream_stream_left_join)) + + table_name_stream_stream_left_join ='stream_stream_left_join' + + ( spark.sql(sql_for_stream_stream_left_join) + .writeStream + #.trigger(availableNow=True) + .queryName(f"write_to_delta_table: {table_name_stream_stream_left_join}") + .option("checkpointLocation", f"{schema_storage_location}/{table_name_stream_stream_left_join}/_checkpoint") + .format("delta") + .toTable(f"{schema_name}.{table_name_stream_stream_left_join}") + ) + +If the stream has finished, then in the next step. You should find that the row count should match up with the regular batch SQL Job. + + spark.read.table(f"{schema_name}.{table_name_stream_stream_left_join}").count() +> **You will find that some records that could not match are not being released, which is expected. **The outer NULL results will be generated with a delay that depends on the specified watermark delay and the time range condition. This is because the engine has to wait for that long to ensure there were no matches and there will be no more matches in future. +> ****Watermark will advance once new data is pushed to it**** + +Thus let’s generate some more fate data to the base table: **vehicle_geo. **This time we are sending a much lower volume of 10 records per second. Let the below command run for at least one batch and then kill it. + + stream_write_to_vehicle_geo_table(rowsPerSecond = 10, numPartitions = 10) + +### 5. b What to observe: + + 1. Soon you should see the watermark moves ahead and the number of records in ‘Aggregation State’ goes down. + + 2. If you click on the running stream and click the raw data tab and look for “watermark”. You will see it has advanced + + 3. Once 0 records per second are being processed, that means your stream has caught up, and now your row count should match up with the traditional SQL left join + + spark.read.table(f"{schema_name}.{table_name_stream_stream_left_join}").count() + +## 6. The cold start edge case: withEventTimeOrder +> “When using a Delta table as a stream source, the query first processes all of the data present in the table. The Delta table at this version is called the initial snapshot. By default, the Delta table’s data files are processed based on which file was last modified. However, the last modification time does not necessarily represent the record event time order. +> In a stateful streaming query with a defined watermark, processing files by modification time can result in records being processed in the wrong order. This could lead to records dropping as late events by the watermark. +> You can avoid the data drop issue by enabling the following option: +> withEventTimeOrder: Whether the initial snapshot should be processed with event time order. + +In our scenario, I pushed this inside Step 3 when we created the temporary streaming views. + + spark.readStream.format("delta") + .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}") + .option("withEventTimeOrder", "true") + .table(f"{schema_name}.{table_name}") + +## 7. Cleanup + +Drop all tables in the database and delete all the checkpoints + + spark.sql( + f""" + drop schema if exists {schema_name} CASCADE + """ + ) + + + dbutils.fs.rm(schema_storage_location, True) + +If you have reached so far, you now have a working pipeline and a solid example which you can use going forward. + +## Download the code + +[https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/spark_stream_stream_join.py](https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/spark_stream_stream_join.py) + +### References: + + 1. [https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#stream-stream-joins](https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#stream-stream-joins) + + 2. [https://youtu.be/hyZU_bw1-ow?t=1181](https://youtu.be/hyZU_bw1-ow?t=1181) + + 3. [https://www.youtube.com/watch?v=1cBDGsSbwRA&t=1500s](https://www.youtube.com/watch?v=1cBDGsSbwRA&t=1500s) + + 4. [https://www.databricks.com/blog/2022/08/22/feature-deep-dive-watermarking-apache-spark-structured-streaming.html](https://www.databricks.com/blog/2022/08/22/feature-deep-dive-watermarking-apache-spark-structured-streaming.html) + + 5. [https://docs.databricks.com/structured-streaming/delta-lake.html#process-initial-snapshot-without-data-being-dropped](https://docs.databricks.com/structured-streaming/delta-lake.html#process-initial-snapshot-without-data-being-dropped) + + +## Footnotes + +If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my [other blogs and follow](https://canadiandataguy.medium.com/). \ No newline at end of file diff --git a/content/blog/youtube.md b/content/blog/youtube.md new file mode 100644 index 000000000..60463df5e --- /dev/null +++ b/content/blog/youtube.md @@ -0,0 +1,15 @@ ++++ +title = "Youtube" +tags = ["coaching","youtube"] +categories = ["coaching"] +banner = "https://images.unsplash.com/photo-1660675588067-13ecd2c19de4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8OXx8aW50ZXJ2aWV3JTIweW91dHViZXxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60" ++++ +{{< youtube id="f7v_1UmkAoM" title="sfs">}} + +------------------------------ +--------------------------------- + +------------------------------ +--------------------------------- + +{{< youtube id="JfDGYKvRfWw" >}} diff --git a/content/contact.md b/content/contact.md new file mode 100644 index 000000000..d0a8bc533 --- /dev/null +++ b/content/contact.md @@ -0,0 +1,8 @@ ++++ +title = "Contact" +id = "contact" + ++++ + +## Do you need Data Expertise? +Does your company have a project that requires data engineering, data architecture or streaming expertise? Don't hesitate to email us. We are happy to answer any questions or requests you may have. We can help with one-off projects or ongoing efforts to advance the shift to a more data-driven culture. Empower leaders and teams better to use data as a tool in the decision-making process. diff --git a/data/aboutSection.yml b/data/aboutSection.yml new file mode 100644 index 000000000..4912eabe2 --- /dev/null +++ b/data/aboutSection.yml @@ -0,0 +1,14 @@ +--- +enable: true +topTitle: About +title: > + ## About me + + A girl who loves to Code and talk about Data +content: > + I'm a self-taught developer who enjoy the ongoing pursuit of knowledge and skills outside of the formal education system. I'm interested in collaborating with people from different background and experience in order to achieve organizational goals. Besides programming, I love cooking, gardening, hiking, and blogging. +# button1Name: Skills +# button2Name: Resume +# button1Target: skill +# button2Target: resume +image: img/chicago.png \ No newline at end of file diff --git a/data/carousel/coaching.yaml b/data/carousel/coaching.yaml new file mode 100644 index 000000000..bc2868532 --- /dev/null +++ b/data/carousel/coaching.yaml @@ -0,0 +1,8 @@ +weight: 2 +title: "Crack the next Data Interview" +description: +
    +
  • Please check out my blogs, YouTube, and advice. If my writing speaks to you and you're looking for personalized guidance. Let's connect.
  • +
+image: "img/carousel/template-easy-code.png" +href: "/blog/1-on-1-coaching" \ No newline at end of file diff --git a/data/carousel/consulting.yaml b/data/carousel/consulting.yaml new file mode 100644 index 000000000..280fb4c57 --- /dev/null +++ b/data/carousel/consulting.yaml @@ -0,0 +1,9 @@ +weight: 1 +title: "We Solve Data Problems" +description: +
    +
  • With over a decade of experience designing data engineering and analytics solutions for startups to large enterprises.
  • +
+ +image: "img/carousel/template-easy-code.png" +href: "/contact" \ No newline at end of file diff --git a/data/hero.yml b/data/hero.yml new file mode 100644 index 000000000..ce86dfe54 --- /dev/null +++ b/data/hero.yml @@ -0,0 +1,29 @@ +--- +enable: true +topTitle: +content: > + # Hello, I'm **Soni** + + #### Real Time Streaming Specialist | Data Architect | Data, Automation And Analytics| Tech Writer + + + I am an end-to-end Full Stack Data Developer who can set up infrastructures, design the architecture, build data pipelines, and productionalize with DevOps. I have spent six years in AWS & Amazon as a Data Architect/Engineer and have a proven record of taking multi-petabyte workloads to production while ensuring minimum operational burden. + + + I also have hands-on experience in Streaming and Batch Analytics, including real-time applications. Having worked remotely for around 6+ years, I’ve learned to avoid the pitfalls of distributed team communication. + + + My goal is to democratize knowledge and help remove the barrier of the hype around Big Data. I write on topics such as real-time data streaming, data architecture, salary negotiation, financial literacy, and more. + + + In my free time, I love to travel so much so that in April 2022, I finished my 12-month-long road trip across Canada. Alright, now click on a few buttons and scroll through the pages to learn more about me and how I can help you. See you on the other side! + + + +# button1Name: My Blog +# button1URL: blog +# button2Name: Download Resume +# button2URL: https://resume-anhcodes.firebaseapp.com/ +image: img/avatar.png +# videoThumb: images/hero/popup-thumb.png +# videoURL: images/hero/signatureupdate.gif diff --git a/data/resumeSection.yml b/data/resumeSection.yml new file mode 100644 index 000000000..25b101f6c --- /dev/null +++ b/data/resumeSection.yml @@ -0,0 +1,105 @@ +--- +enable: true +topTitle: +title: > + ## + +# button1Name: Contact +tab1Name: Experiences +tab1Target: experience +tab2Name: Education & Certifications +tab2Target: education + +education: + - content: > + #### Specialist Data Architect | Databricks + + * Work on solving Streaming & scaling challenges with Big Data across + customers + + * Author blogs with best practices and solution accelerators + + * Enhanced productivity for Audantic by reducing their line of code by + 66% and reduced their development time of Lake House by 86%. + + * Significantly reduced carbon footprint for a customer and co-authored + a blog post detailing design and implementation. + + * Work with strategic customers to solve challenging technical + problems, provide business value, guide product roadmap, and ensure + success on the Databricks platform. + + * Collaborate with teams at large on strategic programs to scale our + organization, design & build internal accelerators, and share best + practices. + + * Hire and mentor new talent + time: 2021-2023 + - content: > + #### Data Engineer/ Data Architect| Amazon/ AWS + + * Built real time data processing platform and CI/CD pipelines with ACID properties while ensuring operational excellence + + * Craft and support 50+ datasets by processing multiple TBs of data + per day serving 600+ Analysts and Economists + + * Responsible for building GDPR & CCPA compliant data lake + + * Strategize customer cloud adoption journey and leverage the + power of the cloud to run big data analytics at scale + + * Interact with customers and identify their pain points across the + system and drive projects to remove them + + * Work with customers as a trusted advisor to drive visibility to best + practices, access to internal resources, expertise on the very latest + features, and accountability for the AWS platform to help them + achieve their business goals and objectives + + * Migrated tables with huge volumes (100-350 TB) from Oracle to + Redshift, by using massively parallel processing architecture which + improved the availability of critical datasets by 96% + + * Interacted with appropriate departments to develop and + accomplish a road map to ensure that systems run smoothly in + projected peak traffic. + time: 2015-2021 + - content: > + #### ETL & Reporting Developer | ZS Associates + * Designed and developed a multi-country (23) data warehouse by + transforming domain knowledge acquired from the client into a + technical artifact. + * Solely responsible for data warehouse performance tuning and + recognized for quickly diagnosing system issues + + time: 2014-2015 + - content: > + #### Business Intelligence Reporting | Tata Consultancy Services + + * Developed Key Performance Indicators for business reports using + SAP Business Objects to drive analytics + + * Awarded with the LIREL honor (Leadership, Integrity, Respect for + individual, Excellence, Learning & sharing), the core 5 values of + Tata Consultancy Services + time: 2012-2014 +experience: + + - content: > + #### Master of Computing Science in Big Data + + **Simon Fraser University, Canada** + time: Aug 2015- May 2017 + - content: > + #### Databricks Developer + time: 2022 + - content: > + #### Azure Fundamentals + time: 2021 + - content: > + #### AWS Developer + time: 2019 + - content: > + #### AWS Solutions Architect + time: 2018 +# buttonTarget: resume \ No newline at end of file diff --git a/data/skillSection.yml b/data/skillSection.yml new file mode 100644 index 000000000..098ddc687 --- /dev/null +++ b/data/skillSection.yml @@ -0,0 +1,30 @@ +--- +enable: true +topTitle: +content: > + ## What is my expertise? + + +image: img/skills-small.jpg + +skill: + - title: Spark, Pyspark + percent: 90 + - title: Real Time Data Streaming (Delta, Kafka, Kinesis, Event Hub) + percent: 90 + - title: SQL, Python + percent: 90 + - title: Databricks, Delta Live Tables + percent: 85 + - title: Database- Redshift, Snwoflake, Big Query, Oracle & Postgres + percent: 80 + - title: Modern Data Stack (Data Build Tool, Dagster & Metabase) + percent: 70 + - title: Continuous Integration & Continuous Deployment ( Github Actions, Azure DevOps ) + percent: 70 + - title: Data Vizualization ( Tableau, Power Bi, Metabase ) + percent: 70 + - title: No-SQL Databases ( Dynamo, Cosmos & Mongo) + percent: 60 + - title: Terraform & CDK + percent: 50 \ No newline at end of file diff --git a/data/testimonials/1.0ala.yaml b/data/testimonials/1.0ala.yaml new file mode 100644 index 000000000..19464a58b --- /dev/null +++ b/data/testimonials/1.0ala.yaml @@ -0,0 +1,4 @@ +text: "Jitesh has played a crucial part in the success of this project. His enthusiasm, dedication, communication, ownership, and technical abilities are simply mind blowing. I don’t believe that I ever worked with someone for a period as short as 10 days that impressed me as much as Jitesh did. A true star of the show." +name: "Ala Qabaja" +position: "Senior Data Engineer, Atlassian" +avatar: "https://media.licdn.com/dms/image/C4E03AQFhLH6uLyhESg/profile-displayphoto-shrink_400_400/0/1579500033911?e=1690416000&v=beta&t=hVUYovnP5vEnav3gzBvUibZkVlpu-lNA7vDBxNF9MDY" \ No newline at end of file diff --git a/data/testimonials/1.sunil.yaml b/data/testimonials/1.sunil.yaml new file mode 100644 index 000000000..d7cbe3bb8 --- /dev/null +++ b/data/testimonials/1.sunil.yaml @@ -0,0 +1,4 @@ +text: "I have had the pleasure of working with Jitesh for 3+ years at Amazon. He is always curious to learn new technologies and implement them when the situation and use case apply. He is an expert at big data technologies and is always looking for ways to build simple solutions to seemingly complex problems. He has built and scaled pipelines that handle petabytes of data and his solutions have seldom resulted in operational issues post-deployment. I am hopeful we will work again!" +name: "Sunil Muktibodh" +position: "Senior Manager, Amazon" +avatar: "https://media.licdn.com/dms/image/C5603AQEQSY-C4IgOhA/profile-displayphoto-shrink_200_200/0/1517430289963?e=1684972800&v=beta&t=LhYQ0H9mikP-YNpDMRHIOUwcxGgt3Gv1Ye_C_A_3lXc" \ No newline at end of file diff --git a/data/testimonials/2.1mike.yaml b/data/testimonials/2.1mike.yaml new file mode 100644 index 000000000..5ebfa555b --- /dev/null +++ b/data/testimonials/2.1mike.yaml @@ -0,0 +1,4 @@ +text: "100% agree. Not only a skilled technical resource, but an absolute pleasure to work with. Just we for-warned, any future support we need from you guys, we’ll be asking for him by name!! " +name: "Mike Henry" +position: "Manager, Data Analytics at ARC Resources Ltd." +avatar: "https://media.licdn.com/dms/image/C4E03AQHTQ3iTjxaByg/profile-displayphoto-shrink_200_200/0/1516317757063?e=1684972800&v=beta&t=nPQo7d9GZuX6ZGi-BLgQroXynagu7FNRXXUwKGMf0xk" \ No newline at end of file diff --git a/data/testimonials/2.Jarriett.yaml b/data/testimonials/2.Jarriett.yaml new file mode 100644 index 000000000..a8784bcd5 --- /dev/null +++ b/data/testimonials/2.Jarriett.yaml @@ -0,0 +1,4 @@ +text: "Jitesh was incredible! His sole goal is to help you improve and he goes above and beyond to achieve that. In addition to his depth and breadth of knowledge, his commitment to the student sets him apart from other coaches. I highly recommend a session with Jitesh." +name: "Jarriett" +position: "Staff Data Engineer" +avatar: "https://media.licdn.com/dms/image/C4D03AQEfiVXe1LPJkQ/profile-displayphoto-shrink_100_100/0/1636400968535?e=1684972800&v=beta&t=yg2wcmlDGw0E8qrtoocKce5tB-h1N5tBBOtsOqxML2o" \ No newline at end of file diff --git a/data/testimonials/2.joel.yaml b/data/testimonials/2.joel.yaml new file mode 100644 index 000000000..8fa6d4674 --- /dev/null +++ b/data/testimonials/2.joel.yaml @@ -0,0 +1,7 @@ +text: "Jitesh was a pleasure to work with on our lakehouse build out project. He excelled at understanding our business logic and requirements, clarifying any ambiguities, and then translating them into code. The technical solutions he created were effective, clean, and easily maintainable. Furthermore, he was receptive to feedback and was open to making adjustments to fit his implementations into our standards and practices. Beyond just the tasks he worked directly on, Jitesh was an effective communicator -- able to proactively provide helpful advice on other items and answer questions about the many technologies he is knowledgeable on. + +In conclusion, I think Jitesh reflects very well on Databricks and I was glad to have the chance to work with him and would definitely do so again in the future. +" +name: "Joel Lowery" +position: "Chief Information Officer, Audnatic" +avatar: "https://media.licdn.com/dms/image/C5603AQH-WPWnXrluhw/profile-displayphoto-shrink_200_200/0/1587956665123?e=1684972800&v=beta&t=cAh9AqO3Cwmg9gOsNf8ZRtbH49YO396HlDEC5c3DbVI" \ No newline at end of file diff --git a/data/testimonials/3.billy.yaml b/data/testimonials/3.billy.yaml new file mode 100644 index 000000000..46c576287 --- /dev/null +++ b/data/testimonials/3.billy.yaml @@ -0,0 +1,4 @@ +text: "Jitesh gave me an excellent overview of what to expect during the final round of Data Engineer interviews at Amazon as well as how best to prepare for them. He absolutely exceeded my expectations for today's session. Highly recommended!" +name: "Billy" +position: "Senior Data Engineer" +avatar: "https://media.licdn.com/dms/image/D5603AQGYq0LgfMXhTw/profile-displayphoto-shrink_200_200/0/1667102056171?e=1684972800&v=beta&t=g6-sG_pEHzMYY-nllqHZRWt82WZBl7gwPqgD-61zJK0" \ No newline at end of file diff --git a/data/testimonials/4.lin.yaml b/data/testimonials/4.lin.yaml new file mode 100644 index 000000000..2cdc0903d --- /dev/null +++ b/data/testimonials/4.lin.yaml @@ -0,0 +1,4 @@ +text: "Jitesh is awesome to work with! He is very knowledgeable and experienced with data engineering concepts, and internal Amazon processes. He has helped me dramatically. Would highly recommend his coaching sessions for anyone interested in a data role at Amazon." +name: "Lin" +position: "Senior Program Manager" +avatar: "https://media.licdn.com/dms/image/C4E03AQFwWty5DOt2Vg/profile-displayphoto-shrink_200_200/0/1565608926475?e=1684972800&v=beta&t=pG746AZ7cfojSwMM7rURgujCon0ShRpNHTixMkFkGTs" \ No newline at end of file diff --git a/data/testimonials/5.blaine.yaml b/data/testimonials/5.blaine.yaml new file mode 100644 index 000000000..306d700ed --- /dev/null +++ b/data/testimonials/5.blaine.yaml @@ -0,0 +1,4 @@ +text: "This was my 3rd session with someone doing DE interviews & this was easily the best one. I liked the guidance to look into high level technical concepts without taking too much time digging into the details - that I can figure out offline on my own." +name: "Blaine" +position: "Senior Software Engineer" +avatar: "https://media.licdn.com/dms/image/C5603AQHAT7VCvaDcKQ/profile-displayphoto-shrink_200_200/0/1520390103963?e=1684972800&v=beta&t=WEC_hvdW4CtwlKfFpPCBD2GxlyfnR9wpjsSZ-s33RRg" \ No newline at end of file diff --git a/data/testimonials/abdullah.yaml b/data/testimonials/abdullah.yaml new file mode 100644 index 000000000..385810580 --- /dev/null +++ b/data/testimonials/abdullah.yaml @@ -0,0 +1,4 @@ +text: "My experience was good with Jitesh, Better than my expectations. It was like a coaching session, in which he guided well." +name: "Abdullah" +position: "Cloud & Data Engineer" +avatar: "https://media.licdn.com/dms/image/D4D03AQEilP6Dqx0XUw/profile-displayphoto-shrink_200_200/0/1674746166561?e=1684972800&v=beta&t=qubr7vJhw9h4Ed9wb2PnvledzkPW02rcISrUZcBnHck" \ No newline at end of file diff --git a/data/testimonials/eugene.yaml b/data/testimonials/eugene.yaml new file mode 100644 index 000000000..2ecd5b314 --- /dev/null +++ b/data/testimonials/eugene.yaml @@ -0,0 +1,4 @@ +text: "Jitesh gave me specific recommendations on my career plan according to what is happening in the data engineering market. I've got detailed and honest answers to my questions. Very valuable session." +name: "Eugene" +position: "Data Engineer" +avatar: "https://media.licdn.com/dms/image/D4E03AQEe1qgpQoXFTA/profile-displayphoto-shrink_200_200/0/1676730312052?e=1684972800&v=beta&t=oxh1GAgtpZvOw1DfVRhl3dyE_qkdhm_rrHuZSsWqP9Q" \ No newline at end of file diff --git a/data/testimonials/minu.yaml b/data/testimonials/minu.yaml new file mode 100644 index 000000000..dbebc1bf1 --- /dev/null +++ b/data/testimonials/minu.yaml @@ -0,0 +1,4 @@ +text: "Jitesh had been very helpful to guide me throughout the interview. He is very quick to respond and goes above and beyond to help. It would not have been possible for me to crack the interview without his guidance. I would highly recommend him." +name: "Minu" +position: "Data Engineer" +avatar: "https://media.licdn.com/dms/image/D5603AQH35zfsFQKDtw/profile-displayphoto-shrink_200_200/0/1664473062733?e=1684972800&v=beta&t=MjN3LtwjWGHQ3a4aEyYdmVn8Ru2pZcr389lKE2aplrg" diff --git a/data/testimonials/nikhil.yaml b/data/testimonials/nikhil.yaml new file mode 100644 index 000000000..3e936d4ef --- /dev/null +++ b/data/testimonials/nikhil.yaml @@ -0,0 +1,4 @@ +text: "Jitesh was amazing. He has a no nonsense approach to things. He really helped in drafting a comprehensive prep plan for DE full time roles and explained it really well. Would definitely recommend him for anyone trying to get into DE roles." +name: "Nikhil" +position: "Data Engineer" +avatar: "https://media.licdn.com/dms/image/C4E03AQFHByb1PkIrcQ/profile-displayphoto-shrink_200_200/0/1651741927202?e=1684972800&v=beta&t=dY6YBaFEULSpOHcN--dJzBpkVTYejAaEjMmQXy1AqtU" \ No newline at end of file diff --git a/data/testimonials/saeed.yaml b/data/testimonials/saeed.yaml new file mode 100644 index 000000000..fb37e895a --- /dev/null +++ b/data/testimonials/saeed.yaml @@ -0,0 +1,4 @@ +text: "This was my 4th session with Jitesh and he always keeps going above and beyond to make sure I need everything I need. With Jitesh's excellent guidance, I managed to pass the phone interview round and we're continuing to prepare for acing the final round. Keep up the great work Jitesh!" +name: "Saeed" +position: "Senior Data Engineer" +avatar: "https://media.licdn.com/dms/image/C5603AQEqwKY0pUFMPw/profile-displayphoto-shrink_200_200/0/1600451468953?e=1684972800&v=beta&t=OrTtzZ5irDnI281Exkk9CYyyn8Mfg42biCxa2BqDlcc" \ No newline at end of file diff --git a/data/testimonials/venkata.yaml b/data/testimonials/venkata.yaml new file mode 100644 index 000000000..07ac4dc70 --- /dev/null +++ b/data/testimonials/venkata.yaml @@ -0,0 +1,4 @@ +text: "Jitesh was extremely good at targeting the DOs and DON'Ts during the interview. His familiarity and broad exposure to systems design and database design concepts reflects the way he guides others in correct decision making during the design interviews. To add to his technical expertise and being up-to-date on the systems design areas, is his profound quality of going above and beyond to help candidates aces through the interviews. He never shuns off from taking the ownership of guiding the candidates throughout the process though he gets paid just for the one hour sessions here. Being extremely approachable is another quality that I like the most in him." +name: "Venkata" +position: "Data Engineer" +avatar: "https://media.licdn.com/dms/image/C4D03AQFdMv5-m_JRtg/profile-displayphoto-shrink_200_200/0/1657740760304?e=1684972800&v=beta&t=VrCn2cSmN1rYW96c5i0wjQUdgjpLiaO_hctl4W1RAOI" \ No newline at end of file diff --git a/database.rules.json b/database.rules.json new file mode 100644 index 000000000..f54493dbd --- /dev/null +++ b/database.rules.json @@ -0,0 +1,7 @@ +{ + /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */ + "rules": { + ".read": false, + ".write": false + } +} \ No newline at end of file diff --git a/firebase.json b/firebase.json new file mode 100644 index 000000000..61b6c7e33 --- /dev/null +++ b/firebase.json @@ -0,0 +1,41 @@ +{ + "database": { + "rules": "database.rules.json" + }, + "firestore": { + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + }, + "storage": { + "rules": "storage.rules" + }, + "emulators": { + "auth": { + "port": 9099 + }, + "functions": { + "port": 5001 + }, + "firestore": { + "port": 8080 + }, + "hosting": { + "port": 5000 + }, + "ui": { + "enabled": true + }, + "singleProjectMode": true + }, + "remoteconfig": { + "template": "remoteconfig.template.json" + } +} diff --git a/firestore.indexes.json b/firestore.indexes.json new file mode 100644 index 000000000..415027e5d --- /dev/null +++ b/firestore.indexes.json @@ -0,0 +1,4 @@ +{ + "indexes": [], + "fieldOverrides": [] +} diff --git a/firestore.rules b/firestore.rules new file mode 100644 index 000000000..c38e3ae32 --- /dev/null +++ b/firestore.rules @@ -0,0 +1,8 @@ +rules_version = '2'; +service cloud.firestore { + match /databases/{database}/documents { + match /{document=**} { + allow read, write: if false; + } + } +} \ No newline at end of file diff --git a/frontmatter.json b/frontmatter.json new file mode 100644 index 000000000..6005997be --- /dev/null +++ b/frontmatter.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://frontmatter.codes/frontmatter.schema.json", + "frontMatter.taxonomy.contentTypes": [ + { + "name": "default", + "pageBundle": false, + "previewPath": null, + "fields": [ + { + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Description", + "name": "description", + "type": "string" + }, + { + "title": "Publishing date", + "name": "date", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "Content preview", + "name": "preview", + "type": "image" + }, + { + "title": "Is in draft", + "name": "draft", + "type": "draft" + }, + { + "title": "Tags", + "name": "tags", + "type": "tags" + }, + { + "title": "Categories", + "name": "categories", + "type": "categories" + } + ] + } + ], + "frontMatter.framework.id": "hugo", + "frontMatter.content.publicFolder": "static", + "frontMatter.content.pageFolders": [ + { + "title": "content", + "path": "[[workspace]]/content", + "originalPath": "[[workspace]]/content" + }, + { + "title": "archetypes", + "path": "[[workspace]]/archetypes", + "originalPath": "[[workspace]]/archetypes" + }, + { + "title": "blog", + "path": "[[workspace]]/content/blog", + "originalPath": "[[workspace]]/content/blog" + }, + { + "title": "layouts", + "path": "[[workspace]]/layouts", + "originalPath": "[[workspace]]/layouts" + }, + { + "title": "archetypes", + "path": "[[workspace]]/layouts/archetypes", + "originalPath": "[[workspace]]/layouts/archetypes" + }, + { + "title": "blog", + "path": "[[workspace]]/exampleSite/content/blog", + "originalPath": "[[workspace]]/exampleSite/content/blog" + }, + { + "title": "archetypes", + "path": "[[workspace]]/themes/hugo-universal-theme/archetypes", + "originalPath": "[[workspace]]/themes/hugo-universal-theme/archetypes" + }, + { + "title": "blog", + "path": "[[workspace]]/themes/hugo-universal-theme/exampleSite/content/blog" + } + ], + "frontMatter.taxonomy.tags": [ + "big data", + "books", + "checkpoint", + "coaching", + "customer stories", + "databricks", + "delta", + "delta live tables", + "foreachBatch", + "go", + "golang", + "hugo", + "interviewing", + "ipsum", + "job_id", + "kafka", + "merge", + "optimize", + "programming", + "run_id", + "spark streaming", + "sql", + "streaming", + "theme", + "workspace", + "youtube", + "z order" + ], + "frontMatter.taxonomy.categories": [ + "Arc Resources", + "Audantic", + "best practices", + "coaching", + "databricks", + "delta", + "delta live tables", + "interviewing", + "jobs", + "lorem", + "optimize", + "power bi", + "programming", + "pseudo", + "spark", + "spark streaming", + "starting", + "streaming" + ] +} \ No newline at end of file diff --git a/layouts/index.html b/layouts/index.html index 96209d460..c5e3c3cf9 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -33,5 +33,17 @@ {{ partial "scripts.html" . }} + + + + + + diff --git a/layouts/page/single.html b/layouts/page/single.html index 09f3fefed..27803a6cf 100644 --- a/layouts/page/single.html +++ b/layouts/page/single.html @@ -23,7 +23,6 @@ {{ else }}
-
diff --git a/layouts/partials/aboutSection.html b/layouts/partials/aboutSection.html new file mode 100644 index 000000000..0001da761 --- /dev/null +++ b/layouts/partials/aboutSection.html @@ -0,0 +1,58 @@ +{{ with .Site.Data.aboutSection }} +{{ if .enable }} +
+
+
+
+
+ {{ .topTitle }} + {{ .title | markdownify}} +
+
+
+
+
+
+
+
+ figure-svg +
+
+ about-img +
+
+
+
+ + + + + + + + + + + +
+
+ {{ .content | markdownify}} +
+ +
+
+
+
+
+
+{{ end }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/about_me.html b/layouts/partials/about_me.html new file mode 100644 index 000000000..b7c8d2fe4 --- /dev/null +++ b/layouts/partials/about_me.html @@ -0,0 +1,30 @@ +
+ +
+
+ +
+ {{ if isset .Site.Params "formspree_action" }} + + {{ partial "hero.html" . }} + {{ partial "skillSection.html" . }} + {{ partial "resumeSection.html" . }} + + {{ end }} + +
+ +
+ + + +
+ + +
+ + + diff --git a/layouts/partials/contact.html b/layouts/partials/contact.html index 557539cbe..f932c59a8 100644 --- a/layouts/partials/contact.html +++ b/layouts/partials/contact.html @@ -66,13 +66,16 @@

{{ i18n "contactAddrTitle" }}

{{ .Site.Params.address | safeHTML }} - + {{ partial "share-buttons.html" . }} {{ end }}
+
+
-{{ partial "map.html" . }} + + diff --git a/layouts/partials/email.svg b/layouts/partials/email.svg new file mode 100644 index 000000000..a78e07350 --- /dev/null +++ b/layouts/partials/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/partials/facebook.svg b/layouts/partials/facebook.svg new file mode 100644 index 000000000..a78e07350 --- /dev/null +++ b/layouts/partials/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 30b0842af..abcf2e0f9 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -55,6 +55,7 @@

{{ i18n "contactTitle" }}

+ {{ partial "share-buttons.html" . }} {{ end }} diff --git a/layouts/partials/hero.html b/layouts/partials/hero.html new file mode 100644 index 000000000..4195a4183 --- /dev/null +++ b/layouts/partials/hero.html @@ -0,0 +1,48 @@ +{{ with .Site.Data.hero }} +{{ if .enable }} +
+ +
+
+
+
+
+
+
+ {{ .topTitle }} + {{ .content | markdownify }} +
+ {{ .buttonName }} +
+
+
+
+ hero-image + + {{ if .videoURL }} +
+
+ popup +
+ + + + + + +
+ {{ end }} +
+
+
+
+
+{{ end }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/linkedin.svg b/layouts/partials/linkedin.svg new file mode 100644 index 000000000..a78e07350 --- /dev/null +++ b/layouts/partials/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/partials/portfolioSection.html b/layouts/partials/portfolioSection.html new file mode 100644 index 000000000..8a321b587 --- /dev/null +++ b/layouts/partials/portfolioSection.html @@ -0,0 +1,34 @@ +{{ with .Site.Data.portfolioSection }} +{{ if .enable }} +
+
+
+
+
+ {{ .topTitle }} + {{ .title | markdownify }} +
+
+
+
+
+
+ {{ range (where $.Site.RegularPages "Type" "portfolio").Reverse }} +
+ portfolio-thumb +
+ {{ .Params.service }} +

+ {{ .Title }} +

+ See details +
+
+ {{ end }} +
+
+
+
+
+{{ end }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/resumeSection.html b/layouts/partials/resumeSection.html new file mode 100644 index 000000000..7b5d44b94 --- /dev/null +++ b/layouts/partials/resumeSection.html @@ -0,0 +1,49 @@ +{{ with .Site.Data.resumeSection }} +{{ if .enable }} +
+
+
+
+
+
+
+ {{ .topTitle }} + {{ .title | markdownify }} +
+ + +
+
+
+
+
+ {{ $Section := .education }} + {{ range $Section }} +
+ {{ .time }} + {{ .content | markdownify }} +
+ {{ end }} + +
+ +
+ {{ $Section := .experience }} + {{ range $Section }} +
+ {{ .time }} + {{ .content | markdownify }} +
+ {{ end }} +
+ +
+
+
+
+
+{{ end }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/serviceSection.html b/layouts/partials/serviceSection.html new file mode 100644 index 000000000..f414575ee --- /dev/null +++ b/layouts/partials/serviceSection.html @@ -0,0 +1,68 @@ +{{ with .Site.Data.serviceSection }} +{{ if .enable }} +
+
+ + + +
+
+ + + + + + + + + + + +
+ +
+ background-pattern +
+ +
+
+
+
+ {{ .topTitle }} + {{ .title | markdownify }} +
+
+
+
+
+
+ {{ $Section := .service }} + {{ range $Section }} +
+
+
+ + + +
+
+ ui-ux +
+
+
+ {{ .content | markdownify }} +
+
+ {{ end }} +
+
+
+
+
+{{ end }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/share-buttons.html b/layouts/partials/share-buttons.html new file mode 100644 index 000000000..b31ff3ff7 --- /dev/null +++ b/layouts/partials/share-buttons.html @@ -0,0 +1,44 @@ +{{/* Config */}} +{{ $switches := (dict + "facebook" false + "twitter" false + "linkedin" false + "whatsapp" true + "xing" false + "mail" true + "medium" true +) }} +{{ $color := "#d5d5d5" }} +{{ $textcolor := "#bbb" }} +{{ $hoverColor := "#444444" }} + +{{/* Code */}} +{{ $url := printf "%s" .Permalink | absLangURL }} +{{ $textBody := print .Title "\n\n" (.Summary | truncate 200) "\n\n" $url "\n" }} +{{ $encodedSummary := strings.TrimPrefix "=" (querify "" (.Summary | truncate 200)) }} + + \ No newline at end of file diff --git a/layouts/partials/sidebar.html b/layouts/partials/sidebar.html index 899ee631b..5284118b6 100644 --- a/layouts/partials/sidebar.html +++ b/layouts/partials/sidebar.html @@ -1,5 +1,6 @@ {{ partial "widgets/search.html" . }} - +{{ partial "share-buttons.html" . }} {{ partial "widgets/categories.html" . }} {{ partial "widgets/tags.html" . }} + diff --git a/layouts/partials/skillSection.html b/layouts/partials/skillSection.html new file mode 100644 index 000000000..2c3ad3b0a --- /dev/null +++ b/layouts/partials/skillSection.html @@ -0,0 +1,46 @@ +{{ with .Site.Data.skillSection }} +{{ if .enable }} +
+ + +
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ {{ .topTitle }} + {{ .content | markdownify }} +
+ {{ $section := .skill }} + {{ range $section }} +
+ {{ .title }} +
{{ .percent }}%
+
+
+
+
+ {{ end }} +
+
+
+
+
+{{ end }} +{{ end }} diff --git a/layouts/partials/socialshare.html b/layouts/partials/socialshare.html new file mode 100644 index 000000000..5cd9a3be5 --- /dev/null +++ b/layouts/partials/socialshare.html @@ -0,0 +1,60 @@ + +{{- if .Param "socialshare" }} + {{ $title := .Title }} + {{ $url := printf "%s" .Permalink }} + {{ $body := print $title ", by " .Site.Title "\n" .Params.description "\n\n" $url "\n" }} + + + {{ end }} +{{- end }} \ No newline at end of file diff --git a/layouts/partials/twitter.svg b/layouts/partials/twitter.svg new file mode 100644 index 000000000..a78e07350 --- /dev/null +++ b/layouts/partials/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/partials/whatsapp.svg b/layouts/partials/whatsapp.svg new file mode 100644 index 000000000..a78e07350 --- /dev/null +++ b/layouts/partials/whatsapp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/shortcodes/medium.html b/layouts/shortcodes/medium.html new file mode 100644 index 000000000..5ecffb90f --- /dev/null +++ b/layouts/shortcodes/medium.html @@ -0,0 +1,7 @@ +{{ $url := .Get "url" }} +{{ with $url }} +
+ + +
+{{ end }} diff --git a/package-lock.json b/package-lock.json index d696eaebc..616109ddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,94 +11,58 @@ "eslint-plugin-promise": "^3.0.0", "eslint-plugin-standard": "^2.0.1", "prettier": "^2.3.2", - "stylelint": "^13.13.1", + "stylelint": "^15.10.1", "stylelint-config-standard": "^22.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", - "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", - "dev": true, + "@babel/highlight": "^7.22.5" + }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/core": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", - "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-compilation-targets": "^7.14.5", - "@babel/helper-module-transforms": "^7.14.8", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, "engines": { "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/highlight": "^7.14.5" + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/core/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=4" } }, - "node_modules/@babel/core/node_modules/chalk": { + "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", @@ -112,450 +76,495 @@ "node": ">=4" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/@babel/generator": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", - "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=4" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", - "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=4" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/@csstools/css-parser-algorithms": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.0.tgz", + "integrity": "sha512-dTKSIHHWc0zPvcS5cqGP+/TPFUJB0ekJ9dGKvMAFoNuBFhDPBt9OMGNZiIA5vTiNdGHHBeScYPXIGBMnVOahsA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^2.1.1" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "node_modules/@csstools/css-tokenizer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", + "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - }, "engines": { - "node": ">=6.9.0" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "node_modules/@csstools/media-query-list-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.2.tgz", + "integrity": "sha512-M8cFGGwl866o6++vIY7j1AKuq9v57cf+dGepScwCcbut9ypJNr4Cj+LLTWligYUZ0uyhEoJDKt5lvyBfh2L3ZQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">=6.9.0" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^2.3.0", + "@csstools/css-tokenizer": "^2.1.1" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "node_modules/@csstools/selector-specificity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz", + "integrity": "sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { - "node": ">=6.9.0" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", - "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "node_modules/@eslint/eslintrc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", + "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.0.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", + "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.10.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz", - "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", - "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=6.9.0" + "node": ">=0.4.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.8" - }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "@babel/types": "^7.14.5" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=6.9.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=6" } }, - "node_modules/@babel/helpers": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz", - "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "node": ">=8" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@babel/parser": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", - "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/@babel/template/node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/@babel/template/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/template/node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/@babel/template/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/@babel/traverse": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", - "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.8", - "@babel/types": "^7.14.8", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "node_modules/camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", "dev": true, "dependencies": { - "@babel/highlight": "^7.14.5" + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/traverse/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=6.9.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@babel/traverse/node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6.9.0" + "node": ">=7.0.0" } }, - "node_modules/@babel/traverse/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/@babel/types": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", - "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "node_modules/css-functions-list": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", + "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.8", - "to-fast-properties": "^2.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=12.22" } }, - "node_modules/@babel/types/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, "engines": { - "node": ">=6.9.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", - "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=4" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -569,689 +578,100 @@ } } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, "engines": { - "node": ">=10.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/@stylelint/postcss-css-in-js": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz", - "integrity": "sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "@babel/core": ">=7.9.0" + "path-type": "^4.0.0" }, - "peerDependencies": { - "postcss": ">=7.0.0", - "postcss-syntax": ">=0.36.2" + "engines": { + "node": ">=8" } }, - "node_modules/@stylelint/postcss-markdown": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz", - "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", - "deprecated": "Use the original unforked package instead: postcss-markdown", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "remark": "^13.0.0", - "unist-util-find-all-after": "^3.0.2" + "esutils": "^2.0.2" }, - "peerDependencies": { - "postcss": ">=7.0.0", - "postcss-syntax": ">=0.36.2" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/@types/mdast": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz", - "integrity": "sha512-YwR7OK8aPmaBvMMUi+pZXBNoW2unbVbfok4YRqGMJBe1dpDlzpRkJrYEYmvjxgs5JhuQmKfDexrN98u941Zasg==", + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "dependencies": { - "@types/unist": "*" + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" } }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", - "dev": true, - "dependencies": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - }, - "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001248", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz", - "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "dependencies": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "dependencies": { - "is-regexp": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.3.792", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.792.tgz", - "integrity": "sha512-RM2O2xrNarM7Cs+XF/OE2qX/aBROyOZqqgP+8FXMXSuWuUqCfUUzg7NytQrzZU3aSqk1Qq6zqnVkJsbfMkIatg==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1261,19 +681,10 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" @@ -1412,23 +823,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1465,18 +859,6 @@ "node": ">=0.10.0" } }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", @@ -1533,24 +915,6 @@ "node": ">=0.10.0" } }, - "node_modules/execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "dependencies": { - "clone-regexp": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1558,9 +922,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1570,7 +934,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -1586,15 +950,18 @@ "dev": true }, "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } }, "node_modules/fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -1625,16 +992,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -1674,27 +1044,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1793,16 +1142,16 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -1813,9 +1162,9 @@ } }, "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -1827,21 +1176,6 @@ "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", "dev": true }, - "node_modules/gonzales-pe": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", - "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "gonzales": "bin/gonzales.js" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -1864,18 +1198,18 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1885,26 +1219,15 @@ } }, "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "engines": { "node": ">=8" - } - }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ignore": { @@ -1948,12 +1271,15 @@ } }, "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/inflight": { @@ -1978,63 +1304,16 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-core-module": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", - "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -2043,16 +1322,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2062,6 +1331,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -2074,16 +1352,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2094,39 +1362,21 @@ } }, "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/isexe": { @@ -2153,18 +1403,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -2183,18 +1421,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2205,9 +1431,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", - "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", + "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", "dev": true }, "node_modules/levn": { @@ -2224,35 +1450,26 @@ } }, "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2262,35 +1479,9 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/longest-streak": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", - "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2304,9 +1495,9 @@ } }, "node_modules/map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true, "engines": { "node": ">=8" @@ -2325,84 +1516,33 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", - "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "longest-streak": "^2.0.0", - "mdast-util-to-string": "^2.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.0.0", - "zwitch": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true }, "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2417,34 +1557,14 @@ "node": ">= 8" } }, - "node_modules/micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" - } - }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -2471,12 +1591,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -2491,41 +1605,44 @@ "node": ">= 6" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - }, "node_modules/normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "dependencies": { "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", + "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" }, @@ -2533,27 +1650,15 @@ "node": ">=10" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-selector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", - "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", - "dev": true - }, - "node_modules/num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2581,39 +1686,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parent-module": { @@ -2628,24 +1727,6 @@ "node": ">=6" } }, - "node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dev": true, - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -2691,12 +1772,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2706,10 +1781,16 @@ "node": ">=8" } }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -2719,54 +1800,33 @@ } }, "node_modules/postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-html": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", - "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", - "dev": true, - "dependencies": { - "htmlparser2": "^3.10.0" - }, - "peerDependencies": { - "postcss": ">=5.0.0", - "postcss-syntax": ">=0.36.0" - } - }, - "node_modules/postcss-less": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", - "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "postcss": "^7.0.14" + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": ">=6.14.4" + "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", - "dev": true - }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", @@ -2774,43 +1834,25 @@ "dev": true }, "node_modules/postcss-safe-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", - "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", "dev": true, - "dependencies": { - "postcss": "^7.0.26" - }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-sass": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", - "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", - "dev": true, - "dependencies": { - "gonzales-pe": "^4.3.0", - "postcss": "^7.0.21" - } - }, - "node_modules/postcss-scss": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", - "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", - "dev": true, - "dependencies": { - "postcss": "^7.0.6" + "node": ">=12.0" }, - "engines": { - "node": ">=6.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -2820,68 +1862,12 @@ "node": ">=4" } }, - "node_modules/postcss-syntax": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", - "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", - "dev": true, - "peerDependencies": { - "postcss": ">=5.0.0" - } - }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/postcss/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss/node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2942,107 +1928,66 @@ ] }, "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", "dev": true, "dependencies": { "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", "dev": true, "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/regexpp": { @@ -3057,56 +2002,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/remark": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", - "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", - "dev": true, - "dependencies": { - "remark-parse": "^9.0.0", - "remark-stringify": "^9.0.0", - "unified": "^9.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", - "dev": true, - "dependencies": { - "mdast-util-from-markdown": "^0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", - "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", - "dev": true, - "dependencies": { - "mdast-util-to-markdown": "^0.6.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3116,19 +2011,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3186,12 +2068,6 @@ "queue-microtask": "^1.2.2" } }, - "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==", - "dev": true - }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -3229,10 +2105,16 @@ } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/slash": { "version": "3.0.0", @@ -3243,19 +2125,36 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -3279,71 +2178,50 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "dev": true, - "bin": { - "specificity": "bin/specificity" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, "dependencies": { - "min-indent": "^1.0.0" + "min-indent": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { @@ -3365,80 +2243,63 @@ "dev": true }, "node_modules/stylelint": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", - "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "version": "15.10.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.1.tgz", + "integrity": "sha512-CYkzYrCFfA/gnOR+u9kJ1PpzwG10WLVnoxHDuBA/JiwGqdM9+yx9+ou6SE/y9YHtfv1mcLo06fdadHTOx4gBZQ==", "dev": true, "dependencies": { - "@stylelint/postcss-css-in-js": "^0.37.2", - "@stylelint/postcss-markdown": "^0.36.2", - "autoprefixer": "^9.8.6", + "@csstools/css-parser-algorithms": "^2.3.0", + "@csstools/css-tokenizer": "^2.1.1", + "@csstools/media-query-list-parser": "^2.1.2", + "@csstools/selector-specificity": "^3.0.0", "balanced-match": "^2.0.0", - "chalk": "^4.1.1", - "cosmiconfig": "^7.0.0", - "debug": "^4.3.1", - "execall": "^2.0.0", - "fast-glob": "^3.2.5", - "fastest-levenshtein": "^1.0.12", + "colord": "^2.9.3", + "cosmiconfig": "^8.2.0", + "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.0", + "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.3", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", - "ignore": "^5.1.8", + "html-tags": "^3.3.1", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.21.0", - "lodash": "^4.17.21", - "log-symbols": "^4.1.0", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.27.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.4", - "normalize-selector": "^0.2.0", - "postcss": "^7.0.35", - "postcss-html": "^0.36.0", - "postcss-less": "^3.1.4", - "postcss-media-query-parser": "^0.2.3", + "meow": "^10.1.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.24", "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^4.0.2", - "postcss-sass": "^0.4.4", - "postcss-scss": "^2.1.1", - "postcss-selector-parser": "^6.0.5", - "postcss-syntax": "^0.36.2", - "postcss-value-parser": "^4.1.0", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.13", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", - "slash": "^3.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.2", - "strip-ansi": "^6.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", "style-search": "^0.1.0", - "sugarss": "^2.0.0", + "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.6.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "table": "^6.8.1", + "write-file-atomic": "^5.0.1" }, "bin": { - "stylelint": "bin/stylelint.js" + "stylelint": "bin/stylelint.mjs" }, "engines": { - "node": ">=10.13.0" + "node": "^14.13.1 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/stylelint" } }, - "node_modules/stylelint-config-recommended": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", - "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", - "dev": true, - "peerDependencies": { - "stylelint": "^13.13.0" - } - }, "node_modules/stylelint-config-standard": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-22.0.0.tgz", @@ -3451,44 +2312,13 @@ "stylelint": "^13.13.0" } }, - "node_modules/stylelint/node_modules/ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/stylelint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/stylelint/node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/stylelint-config-standard/node_modules/stylelint-config-recommended": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", + "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", "dev": true, - "engines": { - "node": ">=8" + "peerDependencies": { + "stylelint": "^13.13.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -3497,96 +2327,15 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "node_modules/stylelint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/stylelint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/stylelint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/stylelint/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/stylelint/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/stylelint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/stylelint/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" } }, - "node_modules/stylelint/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -3596,91 +2345,73 @@ "node": ">=8" } }, - "node_modules/stylelint/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/stylelint/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/stylelint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, - "node_modules/stylelint/node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=10.0.0" } }, - "node_modules/sugarss": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", - "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", - "dev": true, - "dependencies": { - "postcss": "^7.0.2" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/svg-tags": { + "node_modules/table/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "node_modules/text-table": { @@ -3689,15 +2420,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3711,22 +2433,15 @@ } }, "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "dev": true, + "node": ">=12" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/type-check": { @@ -3742,75 +2457,15 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", - "dev": true, - "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-all-after": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", - "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", - "dev": true, - "dependencies": { - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.2" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/uri-js": { @@ -3825,445 +2480,130 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/v8-compile-cache": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", - "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", - "dev": true - }, - "@babel/core": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", - "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-compilation-targets": "^7.14.5", - "@babel/helper-module-transforms": "^7.14.8", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", - "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", - "dev": true, - "requires": { - "@babel/types": "^7.14.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", - "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - } + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true }, - "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { - "@babel/types": "^7.14.5" + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "@babel/types": "^7.14.5" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", - "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "node_modules/word-wrap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, - "requires": { - "@babel/types": "^7.14.5" + "engines": { + "node": ">=0.10.0" } }, - "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, - "@babel/helper-module-transforms": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz", - "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==", + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - }, "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true - } + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "@babel/helper-replace-supers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", - "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" + "engines": { + "node": ">=10" } }, - "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "@babel/types": "^7.14.8" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "requires": { - "@babel/types": "^7.14.5" + "@babel/highlight": "^7.22.5" } }, "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true }, - "@babel/helpers": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz", - "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==", - "dev": true, - "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - } - }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/parser": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", - "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", - "dev": true - }, - "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "color-convert": "^1.9.0" } }, "chalk": { @@ -4276,88 +2616,65 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } - } - } - }, - "@babel/traverse": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", - "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.8", - "@babel/types": "^7.14.8", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "@babel/highlight": "^7.14.5" + "color-name": "1.1.3" } }, - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "has-flag": "^3.0.0" } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true } } }, - "@babel/types": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", - "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "@csstools/css-parser-algorithms": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.0.tgz", + "integrity": "sha512-dTKSIHHWc0zPvcS5cqGP+/TPFUJB0ekJ9dGKvMAFoNuBFhDPBt9OMGNZiIA5vTiNdGHHBeScYPXIGBMnVOahsA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.8", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true - } - } + "requires": {} + }, + "@csstools/css-tokenizer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", + "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", + "dev": true + }, + "@csstools/media-query-list-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.2.tgz", + "integrity": "sha512-M8cFGGwl866o6++vIY7j1AKuq9v57cf+dGepScwCcbut9ypJNr4Cj+LLTWligYUZ0uyhEoJDKt5lvyBfh2L3ZQ==", + "dev": true, + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz", + "integrity": "sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==", + "dev": true, + "requires": {} }, "@eslint/eslintrc": { "version": "1.0.4", @@ -4374,17 +2691,6 @@ "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } } }, "@humanwhocodes/config-array": { @@ -4430,40 +2736,6 @@ "fastq": "^1.6.0" } }, - "@stylelint/postcss-css-in-js": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz", - "integrity": "sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA==", - "dev": true, - "requires": { - "@babel/core": ">=7.9.0" - } - }, - "@stylelint/postcss-markdown": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz", - "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", - "dev": true, - "requires": { - "remark": "^13.0.0", - "unist-util-find-all-after": "^3.0.2" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/mdast": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.7.tgz", - "integrity": "sha512-YwR7OK8aPmaBvMMUi+pZXBNoW2unbVbfok4YRqGMJBe1dpDlzpRkJrYEYmvjxgs5JhuQmKfDexrN98u941Zasg==", - "dev": true, - "requires": { - "@types/unist": "*" - } - }, "@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -4476,18 +2748,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true - }, "acorn": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", @@ -4526,12 +2786,12 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "argparse": { @@ -4549,28 +2809,13 @@ "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, - "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" - } - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "balanced-match": { @@ -4598,19 +2843,6 @@ "fill-range": "^7.0.1" } }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4618,28 +2850,23 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" } }, - "caniuse-lite": { - "version": "1.0.30001248", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz", - "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==", - "dev": true - }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -4648,96 +2875,27 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true - }, - "clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "requires": { - "is-regexp": "^2.0.0" } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, "concat-map": { @@ -4746,26 +2904,16 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, "requires": { - "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "path-type": "^4.0.0" } }, "cross-spawn": { @@ -4779,6 +2927,22 @@ "which": "^2.0.1" } }, + "css-functions-list": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.1.0.tgz", + "integrity": "sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==", + "dev": true + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4786,34 +2950,40 @@ "dev": true }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", "dev": true }, "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "requires": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" }, "dependencies": { + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true } } @@ -4842,59 +3012,10 @@ "esutils": "^2.0.2" } }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "electron-to-chromium": { - "version": "1.3.792", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.792.tgz", - "integrity": "sha512-RM2O2xrNarM7Cs+XF/OE2qX/aBROyOZqqgP+8FXMXSuWuUqCfUUzg7NytQrzZU3aSqk1Qq6zqnVkJsbfMkIatg==", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "enquirer": { @@ -4906,12 +3027,6 @@ "ansi-colors": "^4.1.1" } }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4921,16 +3036,10 @@ "is-arrayish": "^0.2.1" } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint": { @@ -4979,15 +3088,6 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5013,15 +3113,6 @@ } } } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } } } }, @@ -5119,21 +3210,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "requires": { - "clone-regexp": "^2.1.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5141,9 +3217,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -5166,15 +3242,15 @@ "dev": true }, "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -5199,12 +3275,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, @@ -5242,18 +3318,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -5326,23 +3390,23 @@ } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "dependencies": { "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true } } @@ -5353,15 +3417,6 @@ "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", "dev": true }, - "gonzales-pe": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", - "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -5378,40 +3433,26 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -5441,9 +3482,9 @@ "dev": true }, "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true }, "inflight": { @@ -5468,55 +3509,33 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-core-module": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", - "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "requires": { "has": "^1.0.3" } }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -5526,12 +3545,6 @@ "is-extglob": "^2.1.1" } }, - "is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5539,27 +3552,15 @@ "dev": true }, "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, "isexe": { @@ -5583,12 +3584,6 @@ "argparse": "^2.0.1" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5607,12 +3602,6 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -5620,9 +3609,9 @@ "dev": true }, "known-css-properties": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", - "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", + "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", "dev": true }, "levn": { @@ -5636,32 +3625,20 @@ } }, "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5671,23 +3648,7 @@ "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "longest-streak": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", - "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lru-cache": { @@ -5700,9 +3661,9 @@ } }, "map-obj": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", - "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true }, "mathml-tag-names": { @@ -5711,65 +3672,30 @@ "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", "dev": true }, - "mdast-util-from-markdown": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", - "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", - "dev": true, - "requires": { - "@types/mdast": "^3.0.0", - "mdast-util-to-string": "^2.0.0", - "micromark": "~2.11.0", - "parse-entities": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "mdast-util-to-markdown": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", - "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "longest-streak": "^2.0.0", - "mdast-util-to-string": "^2.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.0.0", - "zwitch": "^1.0.0" - } - }, - "mdast-util-to-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", - "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", "dev": true, "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "dependencies": { - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - } + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" } }, "merge2": { @@ -5778,24 +3704,14 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "micromark": { - "version": "2.11.4", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", - "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", - "dev": true, - "requires": { - "debug": "^4.0.0", - "parse-entities": "^2.0.0" - } - }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "min-indent": { @@ -5813,12 +3729,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, "minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -5828,14 +3738,6 @@ "arrify": "^1.0.1", "is-plain-obj": "^1.1.0", "kind-of": "^6.0.3" - }, - "dependencies": { - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - } } }, "ms": { @@ -5844,46 +3746,34 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - }, "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, "requires": { "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", + "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-selector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", - "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", - "dev": true - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "once": { @@ -5910,29 +3800,23 @@ } }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5942,20 +3826,6 @@ "callsites": "^3.0.0" } }, - "parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dev": true, - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -5986,153 +3856,62 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-html": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", - "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", - "dev": true, - "requires": { - "htmlparser2": "^3.10.0" - } - }, - "postcss-less": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", - "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", - "dev": true, - "requires": { - "postcss": "^7.0.14" - } - }, - "postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "postcss-safe-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", - "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", - "dev": true, - "requires": { - "postcss": "^7.0.26" - } - }, - "postcss-sass": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", - "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "requires": { - "gonzales-pe": "^4.3.0", - "postcss": "^7.0.21" + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" } }, - "postcss-scss": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", - "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", "dev": true, - "requires": { - "postcss": "^7.0.6" - } + "requires": {} }, "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dev": true, "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, - "postcss-syntax": { - "version": "0.36.2", - "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", - "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", - "dev": true, - "requires": {} - }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "prelude-ls": { @@ -6166,85 +3945,42 @@ "dev": true }, "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true }, "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", "dev": true, "requires": { "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" } }, "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" } }, "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", "dev": true, "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" } }, "regexpp": { @@ -6253,57 +3989,12 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "remark": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", - "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", - "dev": true, - "requires": { - "remark-parse": "^9.0.0", - "remark-stringify": "^9.0.0", - "unified": "^9.1.0" - } - }, - "remark-parse": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", - "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", - "dev": true, - "requires": { - "mdast-util-from-markdown": "^0.8.0" - } - }, - "remark-stringify": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", - "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", - "dev": true, - "requires": { - "mdast-util-to-markdown": "^0.6.0" - } - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6334,12 +4025,6 @@ "queue-microtask": "^1.2.2" } }, - "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==", - "dev": true - }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -6365,9 +4050,9 @@ "dev": true }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", "dev": true }, "slash": { @@ -6376,16 +4061,27 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -6409,50 +4105,38 @@ } }, "spdx-license-ids": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", - "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", - "dev": true - }, - "specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, "requires": { - "min-indent": "^1.0.0" + "min-indent": "^1.0.1" } }, "strip-json-comments": { @@ -6468,156 +4152,63 @@ "dev": true }, "stylelint": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", - "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "version": "15.10.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.1.tgz", + "integrity": "sha512-CYkzYrCFfA/gnOR+u9kJ1PpzwG10WLVnoxHDuBA/JiwGqdM9+yx9+ou6SE/y9YHtfv1mcLo06fdadHTOx4gBZQ==", "dev": true, "requires": { - "@stylelint/postcss-css-in-js": "^0.37.2", - "@stylelint/postcss-markdown": "^0.36.2", - "autoprefixer": "^9.8.6", + "@csstools/css-parser-algorithms": "^2.3.0", + "@csstools/css-tokenizer": "^2.1.1", + "@csstools/media-query-list-parser": "^2.1.2", + "@csstools/selector-specificity": "^3.0.0", "balanced-match": "^2.0.0", - "chalk": "^4.1.1", - "cosmiconfig": "^7.0.0", - "debug": "^4.3.1", - "execall": "^2.0.0", - "fast-glob": "^3.2.5", - "fastest-levenshtein": "^1.0.12", + "colord": "^2.9.3", + "cosmiconfig": "^8.2.0", + "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.0", + "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.3", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", - "ignore": "^5.1.8", + "html-tags": "^3.3.1", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.21.0", - "lodash": "^4.17.21", - "log-symbols": "^4.1.0", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.27.0", "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.4", - "normalize-selector": "^0.2.0", - "postcss": "^7.0.35", - "postcss-html": "^0.36.0", - "postcss-less": "^3.1.4", - "postcss-media-query-parser": "^0.2.3", + "meow": "^10.1.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.24", "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^4.0.2", - "postcss-sass": "^0.4.4", - "postcss-scss": "^2.1.1", - "postcss-selector-parser": "^6.0.5", - "postcss-syntax": "^0.36.2", - "postcss-value-parser": "^4.1.0", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.13", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", - "slash": "^3.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.2", - "strip-ansi": "^6.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", "style-search": "^0.1.0", - "sugarss": "^2.0.0", + "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.6.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "table": "^6.8.1", + "write-file-atomic": "^5.0.1" }, "dependencies": { - "ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "balanced-match": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "resolve-from": { @@ -6625,61 +4216,9 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - } } } }, - "stylelint-config-recommended": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", - "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", - "dev": true, - "requires": {} - }, "stylelint-config-standard": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-22.0.0.tgz", @@ -6687,24 +4226,34 @@ "dev": true, "requires": { "stylelint-config-recommended": "^5.0.0" + }, + "dependencies": { + "stylelint-config-recommended": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", + "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", + "dev": true, + "requires": {} + } } }, - "sugarss": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", - "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "postcss": "^7.0.2" + "has-flag": "^4.0.0" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" } }, "svg-tags": { @@ -6713,18 +4262,45 @@ "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6735,15 +4311,9 @@ } }, "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", "dev": true }, "type-check": { @@ -6756,58 +4326,11 @@ } }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", - "dev": true, - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - }, - "unist-util-find-all-after": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", - "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", - "dev": true, - "requires": { - "unist-util-is": "^4.0.0" - } - }, - "unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "dev": true, - "requires": { - "@types/unist": "^2.0.2" - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -6820,7 +4343,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "v8-compile-cache": { @@ -6839,28 +4362,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6871,9 +4372,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wrappy": { @@ -6883,15 +4384,13 @@ "dev": true }, "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "requires": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^4.0.1" } }, "yallist": { @@ -6900,23 +4399,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, "yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, - "zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 725997ea9..3813bd4cc 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "eslint-plugin-promise": "^3.0.0", "eslint-plugin-standard": "^2.0.1", "prettier": "^2.3.2", - "stylelint": "^13.13.1", - "stylelint-config-standard": "^22.0.0" + "stylelint": "^15.11.0", + "stylelint-config-standard": "^34.0.0" }, "scripts": { "stylelint": "stylelint \"**/*.css\"", diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..f5cd1c146 --- /dev/null +++ b/public/404.html @@ -0,0 +1,389 @@ + + + + + + + + + +404 Page not found + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+
+ +
+ +
+ +

We are sorry – this page is not here anymore

+

Error 404: Page not found

+ +

Go to homepage +

+
+ + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + diff --git a/public/about-me/index.html b/public/about-me/index.html new file mode 100644 index 000000000..92613ac43 --- /dev/null +++ b/public/about-me/index.html @@ -0,0 +1,736 @@ + + + + + + + + + +About Me + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+

About Me

+
+
+
+
+ + +
+ + +
+ +
+
+ +
+ + + + +
+ +
+
+
+
+
+
+
+ +

Hello, I’m Soni

+

Real Time Streaming Specialist | Data Architect | Data, Automation And Analytics| Tech Writer

+

I am an end-to-end Full Stack Data Developer who can set up infrastructures, design the architecture, build data pipelines, and productionalize with DevOps. I have spent six years in AWS & Amazon as a Data Architect/Engineer and have a proven record of taking multi-petabyte workloads to production while ensuring minimum operational burden.

+

I also have hands-on experience in Streaming and Batch Analytics, including real-time applications. Having worked remotely for around 6+ years, I’ve learned to avoid the pitfalls of distributed team communication.

+

My goal is to democratize knowledge and help remove the barrier of the hype around Big Data. I write on topics such as real-time data streaming, data architecture, salary negotiation, financial literacy, and more.

+

In my free time, I love to travel so much so that in April 2022, I finished my 12-month-long road trip across Canada. Alright, now click on a few buttons and scroll through the pages to learn more about me and how I can help you. See you on the other side!

+ +
+ +
+
+
+
+ hero-image + + +
+
+
+
+
+ + + + +
+ + +
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +

What is my expertise?

+ +
+ + +
+ Spark, Pyspark +
90%
+
+
+
+
+ +
+ Real Time Data Streaming (Delta, Kafka, Kinesis, Event Hub) +
90%
+
+
+
+
+ +
+ SQL, Python +
90%
+
+
+
+
+ +
+ Databricks, Delta Live Tables +
85%
+
+
+
+
+ +
+ Database- Redshift, Snwoflake, Big Query, Oracle & Postgres +
80%
+
+
+
+
+ +
+ Modern Data Stack (Data Build Tool, Dagster & Metabase) +
70%
+
+
+
+
+ +
+ Continuous Integration & Continuous Deployment ( Github Actions, Azure DevOps ) +
70%
+
+
+
+
+ +
+ Data Vizualization ( Tableau, Power Bi, Metabase ) +
70%
+
+
+
+
+ +
+ No-SQL Databases ( Dynamo, Cosmos & Mongo) +
60%
+
+
+
+
+ +
+ Terraform & CDK +
50%
+
+
+
+
+ +
+
+
+
+
+ + + + + +
+
+
+
+
+
+
+ +

+ +
+ + +
+
+
+
+
+ + +
+ 2021-2023 +

Specialist Data Architect | Databricks

+
    +
  • Work on solving Streaming & scaling challenges with Big Data across customers
  • +
  • Author blogs with best practices and solution accelerators
  • +
  • Enhanced productivity for Audantic by reducing their line of code by 66% and reduced their development time of Lake House by 86%.
  • +
  • Significantly reduced carbon footprint for a customer and co-authored a blog post detailing design and implementation.
  • +
  • Work with strategic customers to solve challenging technical problems, provide business value, guide product roadmap, and ensure success on the Databricks platform.
  • +
  • Collaborate with teams at large on strategic programs to scale our organization, design & build internal accelerators, and share best practices.
  • +
  • Hire and mentor new talent
  • +
+ +
+ +
+ 2015-2021 +

Data Engineer/ Data Architect| Amazon/ AWS

+
    +
  • Built real time data processing platform and CI/CD pipelines with ACID properties while ensuring operational excellence
  • +
  • Craft and support 50+ datasets by processing multiple TBs of data per day serving 600+ Analysts and Economists
  • +
  • Responsible for building GDPR & CCPA compliant data lake
  • +
  • Strategize customer cloud adoption journey and leverage the power of the cloud to run big data analytics at scale
  • +
  • Interact with customers and identify their pain points across the system and drive projects to remove them
  • +
  • Work with customers as a trusted advisor to drive visibility to best practices, access to internal resources, expertise on the very latest features, and accountability for the AWS platform to help them achieve their business goals and objectives
  • +
  • Migrated tables with huge volumes (100-350 TB) from Oracle to Redshift, by using massively parallel processing architecture which improved the availability of critical datasets by 96%
  • +
  • Interacted with appropriate departments to develop and accomplish a road map to ensure that systems run smoothly in projected peak traffic.
  • +
+ +
+ +
+ 2014-2015 +

ETL & Reporting Developer | ZS Associates

+
    +
  • Designed and developed a multi-country (23) data warehouse by +transforming domain knowledge acquired from the client into a +technical artifact.
  • +
  • Solely responsible for data warehouse performance tuning and +recognized for quickly diagnosing system issues
  • +
+ +
+ +
+ 2012-2014 +

Business Intelligence Reporting | Tata Consultancy Services

+
    +
  • Developed Key Performance Indicators for business reports using SAP Business Objects to drive analytics
  • +
  • Awarded with the LIREL honor (Leadership, Integrity, Respect for individual, Excellence, Learning & sharing), the core 5 values of Tata Consultancy Services
  • +
+ +
+ + +
+ +
+ + +
+ Aug 2015- May 2017 +

Master of Computing Science in Big Data

+

Simon Fraser University, Canada +

+ +
+ 2022 +

Databricks Developer

+ +
+ +
+ 2021 +

Azure Fundamentals

+ +
+ +
+ 2019 +

AWS Developer

+ +
+ +
+ 2018 +

AWS Solutions Architect

+ +
+ +
+ +
+
+
+
+
+ + + + + +
+ +
+ + + +
+ + +
+ + + + + + +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/authors/index.html b/public/authors/index.html new file mode 100644 index 000000000..5d8bbf474 --- /dev/null +++ b/public/authors/index.html @@ -0,0 +1,656 @@ + + + + + + + + + +Authors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Authors

+
+
+
+
+ + +
+
+
+ + +
+ + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/authors/index.xml b/public/authors/index.xml new file mode 100644 index 000000000..2e64b4c4b --- /dev/null +++ b/public/authors/index.xml @@ -0,0 +1,11 @@ + + + + Authors on Canadian Data Guy + https://canadiandataguy.com/authors/ + Recent content in Authors on Canadian Data Guy + Hugo -- gohugo.io + en-us + + + diff --git a/public/authors/page/1/index.html b/public/authors/page/1/index.html new file mode 100644 index 000000000..8fad8c002 --- /dev/null +++ b/public/authors/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/authors/ + + + + + + diff --git a/public/blog/1-on-1-coaching/index.html b/public/blog/1-on-1-coaching/index.html new file mode 100644 index 000000000..659ca3bb2 --- /dev/null +++ b/public/blog/1-on-1-coaching/index.html @@ -0,0 +1,660 @@ + + + + + + + + + +1 on 1 Interview Coaching + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

1 on 1 Interview Coaching

+
+
+
+
+ + +
+
+ +
+ + + +
+ + + +
+

Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

+ +
+

How can you book a session ?

+

Please have a look at all the free content. Read the blog posts, watch the videos, and read the articles. If you are still interested in working with me, you can book a session with me.

+

You can book me from Stripe or Interview Query or I Got An Offer.

+ +
+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/index.html b/public/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/index.html new file mode 100644 index 000000000..85791e957 --- /dev/null +++ b/public/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/index.html @@ -0,0 +1,786 @@ + + + + + + + + + +Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + April 19, 2023 + +

+ + +
+

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

+

Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios.

+

The below checklist is not ordered, you should aim to check off as many items as you can.

+

+

Beginners best practices checklist for Spark Streaming:

+
    +
  • +

    Choose a trigger interval over nothing at all because it helps control storage transaction api/Listing costs. This is because some Spark jobs have a component which requires a s3/adls listing operation. If our processing is very fast think <1 sec, we will keep repeating these operations and lead to unintended costs. Example .trigger(processingTime=’5 seconds’)

    +
  • +
  • +

    Use ADLS Gen2 on Azure over blob storage as it’s better suited for big data analytics workloads. Read more on the differences here.

    +
  • +
  • +

    Make sure the table partition strategy is chosen carefully and on low cardinality columns like date , region, country, etc. My rough rule of thumb says, if you have more than 100,000 partitions then you have over-partitioned your table. Date columns make a good partition column because they occur naturally. Example for a multinational e-commerce company which operates in 20 countries and wants to store 10 years of data. Once you partition by date & country =( 365 * 10 ) * 20 = you will end up with 73,000 partitions.

    +
  • +
  • +

    Name your streaming query so it is easily identifiable in the Spark UI Streaming tab.

    +
  • +
+

.option(“queryName”, “IngestFromKafka”)

+
(input_stream
+   .select(col("eventId").alias("key"), to_json(struct(col('action'), col('time'), col('processingTime'))).alias("value"))
+   .writeStream
+   .format("kafka")
+   .option("kafka.bootstrap.servers", kafka_bootstrap_servers_plaintext )
+   .option("kafka.security.protocol", "to_be_filled")
+   .option("checkpointLocation", checkpoint_location )
+   .option("topic", topic)
+   .option("queryName", "IngestFromKafka")
+   .start()
+)
+
+
+spark.readStream.format(“kinesis”).**option(“streamName”, stream_name)
+**
+
+
    +
  • +

    Each stream must have its own checkpoint; streams must never share checkpoints. Example, if you have 2 separate streams of different sources and data needs to be written to a single delta table. You should create 2 separate checkpoints and not share a common one. You can find an example with code here.

    +
  • +
  • +

    Don’t run multiple streams on the same driver; if such requirements are there, please benchmark it by running the streams for a few days and watch for stability over driver-related issues. Multiplexing on the same cluster is generally not recommended.

    +
  • +
  • +

    Partition size of data in memory should be between 100–200MB. Use Spark UI and alter maxFilesPerTrigger & maxBytesPerTrigger to achieve these partition sizes of around 100–200 MB.

    +
  • +
  • +

    Check if a sort merge join can be changed to Broadcast hash join. Only possible if the dataset being joined is small. The dataset being broadcasted should be around 100 MB. Increase auto-broadcast hash join threshold to 1gb if needed try a bigger instance family.

    +
  • +
+

Advanced best practices checklist for Spark Streaming:

+
    +
  • +

    Establish a naming convention for your checkpoint: Over the course of the life of your table, you will end up having multiple checkpoints due to application upgrades, logic changes, etc. Give your checkpoint a meaningful name something which tells the following:

    +
      +
    • Target table name
    • +
    • Starting timestamp: When the checkpoint came into existence
    • +
    • Example naming conventions can be : +
        +
      • Generic {table_location}/checkpoints/{target_table_name}_starting_timestamp{_actual_timestamp}
      • +
      • If source is Delta then use startingVersion {table_location}/checkpoints/{target_table_name}_startingVersion{_startingVersion}
      • +
      +
    • +
    +
  • +
  • +

    See Shuffle Spill (Disk) on Spark UI to be as minimum as possible. Ideally zero only shuffle read should be there. Shuffle spill disappears from UI if it’s zero.

    +
  • +
  • +

    Use rocks DB , if there are stateful transformations.

    +
  • +
  • +

    Prefer to Azure Event Hub using it it’s Kafka connector. For Azure EventHubs, the number of cores must be == to number of partitions. With Kafka connector it is different, as it can split Kafka partition into multiple Spark partitions, and this is one of the reasons to go with Kafka protocol on EventHubs.

    +
  • +
  • +

    If there is state always have a watermark so it can clean itself. In case you need to have infinite state, recommend you to store that in a Delta table and zorder on necessary column so lookups are fast.

    +
  • +
  • +

    At big scale, think close to Trillions of records in state store. If there is a deduplicate requirement, use delta merge approach over drop duplicate to avoid state store growing very large.

    +
  • +
  • +

    Azure instance family choices:

    +
      +
    • F-series for map-heavy streams — parsing, json deserialization, etc.
    • +
    • Fsv2-series if doing multiple streams from the same source or need a little spill space.
    • +
    • DS_v2-series for streams that join tables or do aggregations. Also for delta optimize (both bin-packing and Z-Ordering) scheduled jobs
    • +
    • L series has direct attached SSD which helps Delta caching
    • +
    +
  • +
  • +

    Don’t set the sql.shuffle.partitions too high — ideally they should be set to be equal to the total number of worker cores. You will need to clear the check point if it is not changing. It is because checkpoint has stored this information and is using that.

    +
  • +
+

References:

+
    +
  1. +

    best practices from this page

    +
  2. +
  3. +

    youtube video with tips on how to scale at production

    +
  4. +
+

Footnote:

+

Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website **CanadianDataGuy.com** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/index.html b/public/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/index.html new file mode 100644 index 000000000..aef75b31d --- /dev/null +++ b/public/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/index.html @@ -0,0 +1,763 @@ + + + + + + + + + +A Productive Life: How to Parallelize Code Execution in Python + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

A Productive Life: How to Parallelize Code Execution in Python

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + April 23, 2023 + +

+ + +
+

A Productive Life: How to Parallelize Code Execution in Python

+

Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code.

+

In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. We will explore 3 examples here:

+

Example 1: Asyncio Tasks / create_task()

+

In asyncio, a task is a unit of work that is scheduled to run on the event loop. Tasks are created from coroutines, which are functions that are defined using the async def syntax and that can suspend their execution using the await keyword.

+

To create a task, we use the asyncio.create_task() function, which takes a coroutine as its argument and returns a Task object. We can then schedule the task to run on the event loop using the await keyword.

+

Here’s an example:

+
import asyncio
+
+async def function_which_will_run_in_parallel():
+    # Add what you want to do here
+    print('function_which_will_run_in_parallel completed')
+
+# Orchestrate function 
+async def main():
+    task = asyncio.create_task(function_which_will_run_in_parallel())
+    await task
+
+asyncio.run(main())
+
+

In this example, we define a simple function_which_will_run_in_parallel() that waits for one second and then prints a message. In the main() function, we create a Task object using asyncio.create_task() and pass it the function.

+

We then await the completion of the task using await task. When we run the main() using asyncio.run(), the Task object is created and scheduled on the event loop, which runs the function_which_will_run_in_parallel() function asynchronously. Once the function_which_will_run_in_parallel() function is complete, the Task object is marked as done, and the program exits.

+

Photo by Sarah Dorweiler on Unsplash

+

Example 2: Running Tasks in Parallel

+

In asyncio, we can run tasks in parallel using the asyncio.gather() function. This function takes one or more coroutines/functions as its arguments and returns a list of their results.

+

Here’s an example:

+
import asyncio
+
+async def coroutine1():
+    await asyncio.sleep(1)
+    return 'Coroutine 1 completed'
+async def coroutine2():
+    await asyncio.sleep(2)
+    return 'Coroutine 2 completed'
+
+async def main():
+    results = await asyncio.gather(coroutine1(), coroutine2())
+    print(results)
+asyncio.run(main())
+
+

In this example, we define two coroutines, coroutine1() and coroutine2(), that wait for one and two seconds, respectively, before returning a message.

+

In the main() coroutine function, we use the asyncio.gather() function to run the two coroutines in parallel. We pass coroutine1() and coroutine2() as its arguments and use await to wait for both coroutines to complete.

+

When both coroutines are complete, the asyncio.gather() function returns a list of their results, which we print to the console.

+

Example 3: Running tasks in parallel with a loop

+

In this example, we define an asynchronous coroutine function fetch() that uses the aiohttp library to download the contents of a given URL. We then define the main() coroutine function that creates a list of URLs to download and uses asyncio.create_task() to create a task for each URL. We then use asyncio.gather() to wait for all tasks to complete and return their results.

+

The async with aiohttp.ClientSession() context manager is used to create a session object that can be reused across multiple requests, which can improve performance.

+

When we run the main() coroutine using asyncio.run(), it concurrently downloads the web pages from the list of URLs, and prints the number of bytes downloaded from each URL.

+

This is just a simple example, but it demonstrates how asyncio can be used to concurrently perform I/O-bound tasks such as web page downloads.

+
import asyncio
+import aiohttp
+
+async def fetch(session, url):
+    async with session.get(url) as response:
+        return await response.text()
+
+async def main():
+    urls = [
+        'https://example.com',
+        'https://google.com',
+        'https://facebook.com',
+        'https://twitter.com',
+        'https://linkedin.com',
+    ]
+
+    async with aiohttp.ClientSession() as session:
+        tasks = [asyncio.create_task(fetch(session, url)) for url in urls]
+
+        results = await asyncio.gather(*tasks)
+
+        for url, result in zip(urls, results):
+            print(f"Downloaded {len(result)} bytes from {url}")
+
+asyncio.run(main())
+
+

Conclusion

+

Asyncio is a powerful framework for writing asynchronous code in Python, and with the new features introduced in Python 3.10, it has become even easier to run tasks in parallel.

+

In this blog post, we learned how to create tasks using asyncio.create_task() and how to run tasks in parallel using `asyncio.gather()

+

Footnote:

+

Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website **CanadianDataGuy.com** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/index.html b/public/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/index.html new file mode 100644 index 000000000..2dc351595 --- /dev/null +++ b/public/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/index.html @@ -0,0 +1,728 @@ + + + + + + + + + +How to Cut Your Data Processing Costs by 30% with Graviton + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to Cut Your Data Processing Costs by 30% with Graviton

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + April 23, 2023 + +

+ + +
+

How to Cut Your Data Processing Costs by 30% with Graviton

+

What is AWS Graviton ?

+

AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

+

With Graviton, you can enjoy lightning-fast data processing speeds while saving money on your infrastructure costs. Plus, Graviton is compatible with all your favorite tools and applications, so you can seamlessly integrate it into your existing workflow.

+

Overall, AWS Graviton offers a flexible and cost-effective alternative to traditional x86-based processors, making it a popular choice for customers who are looking to optimize their cloud computing costs without sacrificing performance or reliability.

+

Cost Savings

+

If you look at the screenshot below, you will find Graviton cheaper than every other series.

+

**Decipher instance name: c6g.xlarge: **C stands for compute series, 6 stands for a series number, g stands for Graviton, and xLarge means 4 vCPU.

+

Compute Intensive (C Series)

+

c6g.xlarge is 12.5% cheaper than the next cheapest instance.

+

+

General Purpose (M Series)

+

m6g.xlarge is ~12.2% cheaper than the next cheapest instance.

+

+

Memory Intensive ( R Series)

+

r6g.xlarge is ~12.5% cheaper than the next cheapest instance.

+

+

This is complicated. Help me choose ?

+

Let me break down the AWS instance series into simple parts. Think about how much memory you get per core, and the price increases as the memory increases.

+
    +
  • +

    Compute intensive C series provides 2GB memory per core.

    +
  • +
  • +

    General purpose M series provides 4GB memory per core and is 13% more expensive than the C series.

    +
  • +
  • +

    Memory-intensive R series provides 8 GB memory per core and is 30% more expensive than the M series. ~48% more expensive than the C series.

    +
  • +
+

I recommend that customers start with general purpose, get a baseline runtime, and then try different series. The best way to gauge what instance family would work is to identify or categorize if the workload is compute-bound, memory-bound or network bound.

+

Launch of new Graviton 3 series in 2023

+

Here are some benefits of the new Graviton 3 series; the price is ~10% more expensive Graviton 2. However, it’s still cheaper than the M6 a instance.

+
+

M6g ($ 0.154) < M7g ($ 0.1632) < M6a ( $0.1728 )

+
+

+New Graviton3-Based General Purpose (m7g) and Memory-Optimized (r7g) Amazon EC2 Instances | Amazon… +*We’ve come a long way since the launch of the m1.small instance in 2006, adding instances with additional memory…*aws.amazon.com

+

Conclusion

+
+

As you can see, the price saving is at least ~12%, and AWS claims 40% better price performance due to faster processors. Thus, in reality, you should be able to save 12–40% cost savings at least. In my real-world experience, I have seen 20–30% cost savings.

+
+

Footnote:

+

Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website **CanadianDataGuy.com** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/index.html b/public/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/index.html new file mode 100644 index 000000000..eb7734aeb --- /dev/null +++ b/public/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/index.html @@ -0,0 +1,832 @@ + + + + + + + + + +Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + June 6, 2023 + +

+ + +
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+

Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function.

+

Photo by Andrew Schultz on Unsplash

+

Spark Streaming & foreachBatch

+

Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. This can be very useful for applications that need to respond to changes in data in real time.

+

One of the features of Spark Streaming is the foreachBatch() method. This method allows you to apply a custom function to each batch of data as it arrives. This can be useful for a variety of tasks, such as:

+
    +
  • +

    Filtering data

    +
  • +
  • +

    Transforming data

    +
  • +
  • +

    Writing data to a database

    +
  • +
  • +

    Sending data to an external system

    +
  • +
+

The foreachBatch() method is a powerful tool that can be used to extend the capabilities of Spark Streaming. In this blog post, we will take a closer look at how to use foreachBatch().

+

Introducing foreachBatch:

+

foreachBatch is a method provided by Spark Streaming that allows developers to apply arbitrary operations on the output of a streaming query. It acts as a bridge between the streaming world and the structured world of DataFrames and Datasets. This means that we can leverage the rich functionality of Spark’s structured APIs to process real-time data efficiently.

+

The Power of foreachBatch:

+

The foreachBatch operation enables developers to perform batch-like operations on streaming data. Instead of processing each individual record, which can be inefficient, foreachBatch processes the data in micro-batches, offering better performance and resource utilization. This approach also provides the flexibility to leverage the full power of Spark’s DataFrames, including various transformations and aggregations, to perform complex computations on streaming data.

+

Implementing foreachBatch:

+

To use foreachBatch, you need to define a function that takes two arguments: the batch identifier and the DataFrame representing the micro-batch of data. Inside this function, you can apply any transformations or computations required on the streaming data. You can use Spark’s SQL, DataFrame, or Dataset APIs to manipulate the data and write the results to any external systems, such as databases or file systems.

+

Benefits of foreachBatch:

+
    +
  1. +

    Performance: foreachBatch allows batch-like processing on streaming data, resulting in improved performance compared to processing individual records.

    +
  2. +
  3. +

    Flexibility: Leveraging Spark’s DataFrames and Datasets provides a wide range of transformations and aggregations to handle complex computations easily.

    +
  4. +
  5. +

    Scalability: Spark Streaming inherently provides scalability and fault-tolerance, and foreachBatch seamlessly integrates with these capabilities.

    +
  6. +
  7. +

    Ecosystem Integration: The results from foreachBatch can be easily written to external systems such as databases, file systems, or streaming analytics platforms.

    +
  8. +
+

Code & Setup

+

Here’s how we can use foreachBatch to achieve this:

+

∘ Define parameters for the job +∘ Create a Streaming source +∘ Define custom processing logic and parameters +∘ Create an instance of forEachBatchProcessor Class with the parameters +∘ Orchestrate the job +∘ Look at the output table +∘ Clean Up

+

Define parameters for the job

+
target_table_name = "for_each_batch_paramerterize"
+check_point_location = f"/tmp/delta/{target_table_name}/_checkpoints/"
+dedupe_colum_name ="hash"
+
+

Create a Streaming source

+

We will create a synthetic dataset.

+
generated_df = (
+     spark.readStream
+        .format("rate")
+        .option("numPartitions", 4)
+        .option("rowsPerSecond", 1 * 1000)
+        .load()
+        .selectExpr(
+          "md5( CAST (value AS STRING) ) as md5"
+          ,"value"
+          ,"value%1000000 as hash"
+        )
+)
+
+

Define custom processing logic and parameters

+
class forEachBatchProcessor:
+    def __init__(self, dedupe_column: str, filter_criteria:str, passed_value: int):
+        self.dedupe_column = dedupe_column
+        self.filter_criteria = filter_criteria
+        self.passed_value = passed_value
+
+    def print_attributes(self):
+        attributes = vars(self)
+        print(
+            "\n".join([f"{attr}: {value}" for attr, value in attributes.items()])
+        )
+
+    def make_changes_using_the_micro_batch(self, microBatchOutputDF, batchId: int):
+        self.print_attributes()
+        print(f"Processing batchId: {batchId}")
+
+        # Your processing logic using the parameter
+        view_name = f"updates_for_batchId_{batchId}"
+        microBatchOutputDF.createOrReplaceTempView(view_name)
+        sql_logic = f"""
+            SELECT 
+                * 
+                ,{self.passed_value} as passed_value
+                ,{batchId} as batch_id
+            FROM (
+              SELECT *
+                ,rank() over(partition by {self.dedupe_column} order by value desc) as dedupe
+              FROM {view_name}
+              WHERE  
+                {self.filter_criteria}
+              )
+            WHERE 
+                dedupe =1 
+        """
+        print(f"Processing sql_logic: {sql_logic}")
+        to_be_written_df = microBatchOutputDF.sparkSession.sql(sql_logic).drop("dedupe")
+        to_be_written_df.write.mode("append").saveAsTable(target_table_name)
+
+

Create an instance of forEachBatchProcessor Class with the parameters

+
instantiateForEachBatchProcessor = forEachBatchProcessor(
+            dedupe_column = dedupe_colum_name,
+            filter_criteria = "1=1",
+            passed_value = 3
+        )
+
+

Orchestrate the job

+
(
+  generated_df
+ .writeStream
+ #.trigger(availableNow=True) 
+ .trigger(processingTime='10 seconds')
+ .option("checkpointLocation", check_point_location)
+ .option("queryName", "ParameterizeForEachBatch")
+ .foreachBatch(instantiateForEachBatchProcessor.make_changes_using_the_micro_batch)
+ .start()
+)
+
+

Look at the output table

+
display(spark.read.table(target_table_name))
+
+

Clean Up

+
spark.sql(f"""
+          DROP TABLE IF EXISTS {target_table_name}
+          """)
+dbutils.fs.rm(check_point_location,True)
+
+

Conclusion:

+

Apache Spark Streaming’s foreachBatch operation is a powerful tool for simplifying real-time data processing. By bridging the gap between the streaming and structured worlds, it enables developers to perform batch-like operations on streaming data efficiently. Leveraging the rich functionality of Spark’s DataFrames, foreachBatch empowers users to process and analyze real-time data with ease. Whether you’re performing aggregations, transformations, or writing data to external systems, foreachBatch offers a flexible and scalable solution for real-time streaming applications.

+

Footnote:

+

Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website **CanadianDataGuy.com** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work.

+

Download the code

+
+

I want to emphasize that my blog posts are designed to be practical resources that you can readily use in your own environments. By providing code examples with careful attention to best practices, I aim to simplify the implementation of real-time data processing solutions. I encourage you to explore the blog, copy the code snippets, and adapt them to your specific needs. With these resources, you’ll be equipped to accelerate your development process and unlock the power of Spark Streaming. Dive in, leverage the code, and start building your real-time data processing pipelines with confidence! +Go Build! +Canadian Data Guy!

+
+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/index.html b/public/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/index.html new file mode 100644 index 000000000..e5a6cee19 --- /dev/null +++ b/public/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/index.html @@ -0,0 +1,721 @@ + + + + + + + + + +Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + September 12, 2023 + +

+ + +
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+

In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? And how can we optimize it to ensure our databases aren’t just fast, but consistently reliable for 99% of our users? Join us as we demystify P99 latency and delve into strategies to fine-tune it in Databricks SQL.

+

Photo by Håkon Sataøen on Unsplash

+

What is P99 Latency?

+

The P99 latency (also known as the 99th percentile latency) for SQL queries is a metric used to measure the response time of SQL queries in a database system. It represents the latency at which 99% of the queries have a response time less than or equal to the P99 latency value, and 1% have a response time greater than the P99 latency value.

+
+

In other words, P99 latency helps you understand the worst-case response time for most of your SQL queries. It is often used to evaluate the performance of a database system and ensure that the vast majority of queries are responding quickly, even under heavy load.

+
+

For example, if the P99 latency for a particular SQL query is 100 milliseconds, it means that 99% of the time, that query will execute in 100 milliseconds or less. However, in 1% of cases, it may take longer than 100 milliseconds.

+

To achieve a P99 latency of 5 seconds in Databricks SQL, you can follow these steps:

+
    +
  1. +

    Optimize the table hourly by applying a WHERE filter on the timestamp.

    +
  2. +
  3. +

    Use at least a Medium instance of DBSQL. Larger instances provide better query performance if queries rely on caching. Caching is done at the instance level; if you have N small instances, then you will have N copies of Cached data occupying memory.

    +
  4. +
  5. +

    Set spark.databricks.delta.stalenessLimit to x minutes per business requirements. This parameter defines how many minutes of old data are acceptable.

    +
  6. +
  7. +

    Ensure that the columns used in the WHERE clause are part of the first 32 columns.

    +
  8. +
  9. +

    Run VACUUM tables at daily or weekly cadence.

    +
  10. +
  11. +

    Once you have achieved your P99 latency with the above suggestions, try DBSQL Serverless and monitor P99 latency. Serverless DB Sql would give you faster startup times and more performance because of faster auto-scaling capabilities.

    +
  12. +
+
+

If you need to power an application with minimum latency, it’s possible to pre-cache data using specific commands. However, it’s important to take caution while using these commands as misconfiguration can cause more harm than good. It’s recommended to reach out to me or your Databricks representative for the command and have the scenario validated with Databricks before implementing it. I have not included the command in the blog to avoid any mishaps.

+
+

Reference:

+
    +
  1. spark.databricks.delta.stalenessLimit https://docs.databricks.com/en/delta/best-practices.html#manage-data-recency
  2. +
+ +

3. https://www.youtube.com/watch?v=rJDkfRPUebw&t=629s

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/index.html b/public/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/index.html new file mode 100644 index 000000000..fa7145f3d --- /dev/null +++ b/public/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/index.html @@ -0,0 +1,759 @@ + + + + + + + + + +Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + September 15, 2023 + +

+ + +
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Them

+

Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards.

+

Photo by Romain Gal on Unsplash

+

1. Getting Started with Viewing and Organizing Dashboards:

+
    +
  • +

    Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. By default, your dashboards will appear in the Home folder. Alternatively, click on “Dashboards” in the sidebar.

    +
  • +
  • +

    Organizing Dashboards: They are sorted in reverse chronological order, but you can reorder them by clicking column headings. Starting July 10, 2023, Databricks will migrate all SQL content to the workspace browser, so ensure your dashboards are in order!

    +
  • +
+

2. Tags are your friend

+
    +
  • +

    Organization: Tags allow users to categorize and organize their SQL objects, such as queries, dashboards, and visualizations. This makes finding and managing these objects easier, especially in large environments. Some good tag examples are customers, departments, projects, environments, etc.

    +
  • +
  • +

    Access Control: Tags can be used in conjunction with access control policies. By tagging SQL objects, administrators can define fine-grained access controls based on these tags, ensuring that only authorized users can access or modify specific resources.

    +
  • +
  • +

    Cost Allocation: Databricks allow you to track cloud costs associated with running queries. By tagging SQL queries or jobs, organizations can allocate costs to specific departments, projects, or teams, providing better visibility into resource consumption.

    +
  • +
+

3. Cloning: Replicating Success:

+
    +
  • If you’ve created a dashboard you’re particularly proud of, clone it! Ensure you have the necessary permissions; thenew dashboard will list you as its owner. Note: Sharing settings, alerts, and subscriptions won’t be copied to the new dashboard.
  • +
+

4. Harnessing the Power of Query Parameters:

+
    +
  • +

    Parameter Types: Your queries can use parameters or static values. Depending on your needs, visualizations based on parameterized queries can be set to use widget parameters, dashboard parameters, or static values.

    +
  • +
  • +

    Multiple Visualizations: One of the strengths of Databricks is that dashboard parameters can apply to multiple visualizations, offering a cohesive data representation.

    +
  • +
+

5. Editing and Customizing Your Dashboard:

+
    +
  • +

    Making Changes: Dashboards can be edited to add or remove content, edit visualizations, and apply filters. Dive in and make it truly your own!

    +
  • +
  • +

    Colour Customization: Stand out with unique dashboard colours. Create your own color palettes or use the default ones to make your dashboard visually appealing.

    +
  • +
+

6. Keeping Your Data Fresh with Refreshes:

+
    +
  • +

    Why Refresh?: Dashboards fetch data from a cache. To ensure up-to-date data representation, it’s crucial to refresh them periodically.

    +
  • +
  • +

    Manual vs. Automatic: Depending on your needs, dashboards can be refreshed manually or set to refresh automatically on a schedule.

    +
  • +
+

7. Stay Updated with Dashboard Subscriptions:

+
    +
  • +

    Snapshot Subscriptions: Subscribe to dashboard snapshots and receive periodic email updates. This is especially useful for keeping stakeholders in the loop.

    +
  • +
  • +

    File Size Limit: Remember there’s a 6 MB file size limit for email attachments. If your dashboard is data-heavy, subscribers will receive a link to the refreshed dashboard instead.

    +
  • +
+

8. Managing and Optimizing Dashboards:

+
    +
  • +

    Download, Trash, Restore: If needed, dashboards can be downloaded as PDFs, moved to Trash, or even restored.

    +
  • +
  • +

    Admin Privileges: If you’re a workspace admin user, you can access an admin view to oversee all dashboards and manage permissions effectively.

    +
  • +
  • +

    Transferring Ownership: Need to hand over a dashboard to another team member? No problem! Ownership can be easily transferred.

    +
  • +
+

In conclusion, Databricks SQL dashboards offer a versatile data visualization and analysis platform. With this step-by-step guide, you’re well on your way to mastering the art of creating, managing, and optimizing your dashboards. Dive in and explore the world of data with Databricks!

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html b/public/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html new file mode 100644 index 000000000..18d1bc01d --- /dev/null +++ b/public/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html @@ -0,0 +1,805 @@ + + + + + + + + + +Solving Delta Table Concurrency Issues + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Solving Delta Table Concurrency Issues

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + September 30, 2023 + +

+ + +
+

Solving Delta Table Concurrency Issues

+

Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

+

Why Do These Issues Happen?

+
    +
  • +

    Concurrent Append Exception (ConcurrentAppendException):** +This error happens when another operation is adding files to the same section (or any section in a table without partitions) that your operation is reading from. These file additions can happen due to INSERT, DELETE, UPDATE, or MERGE operations. By default, with the WriteSerializable isolation level, adding files without checking any data (known as blind INSERT operations) won’t cause any issues with any operation, even if they are working on the same section (or any section in a table without partitions). However, if the isolation level is changed to Serializable, then these blind additions may cause conflicts. This error is commonly seen during simultaneous DELETE, UPDATE, or MERGE operations. Even though these operations might be updating different sections, a conflict can occur if one operation is reading the same section that another operation is updating at the same time.

    +
  • +
  • +

    Concurrent Delete Read Exception: +It occurs when a transaction is trying to read a file that is being deleted by another transaction. This is to ensure that a transaction does not read data that is in the process of being deleted.

    +
  • +
  • +

    Concurrent Delete Delete Exception: +— Occurs when two transactions are trying to delete the same file. +— Delta Lake ensures that a file is not deleted more than once.

    +
  • +
+

Understanding Isolation Levels: Serializable vs. WriteSerializable

+

Isolation levels in a database control how much transactions are protected from each other’s changes. Delta Lake on Databricks offers two such levels: Serializable and WriteSerializable.

+

1. Serializable: +— This is the highest level of isolation. +— It ensures that all write and read operations are done in a specific order, just like how they appear in the table’s history. +— This means operations are carried out one by one, maintaining the order and ensuring the final result is as expected.

+

2. WriteSerializable (Default): +— This level is a bit more relaxed compared to Serializable. +— It guarantees order only for write operations, not for reads. +— Even though it’s more relaxed, it’s still more strict than the Snapshot isolation level. +— This level is used by default as it offers a good mix of data consistency and availability for most operations.

+

Solution 1: Setting the Isolation Level:

+
    +
  • +

    Use the ALTER TABLE command to set the isolation level to Serializable or WriteSerializable.

    +

    ALTER TABLE SET TBLPROPERTIES (‘delta.isolationLevel’ = ‘WriteSerializable’)

    +
  • +
+

Solution 2: Avoiding Conflicts Using Partitioning and Disjoint Command Conditions

+

When working with tables, sometimes two operations can clash or conflict, especially if they are working on the same set of files. This can cause problems and errors. But, there’s a way to avoid this! You can organize or partition your table based on certain columns that are often used in operations. This way, different operations work on different parts of the table, preventing them from clashing.

+

For example, imagine two commands — one is updating the table for dates after January 1, 2010, and another is deleting from the table for dates before January 1, 2010. These two can clash if the table is not organized by date, as both might try to change the same files. But if you partition the table by date, these operations won’t conflict, making things smooth and error-free.

+

However, be careful while choosing the column for partitioning. If you choose a column that has a lot of unique values, it can create a large number of subdirectories. This can lead to other issues, affecting the performance of operations on the table.

+

By using these strategies and understanding the insights from Databricks regarding isolation levels, row-level concurrency, and write conflicts, you can make your Delta operations more robust, reliable, and efficient.

+

Solution 3: Code block with exponential retry

+

The Python code below offers a robust solution to address this challenge. It is designed to manage concurrent write operations to a Delta table or path by intelligently retrying the operation in the event of specific concurrent exceptions. Streaming_write_with_concurrent_retry takes parameters such as the data stream, maximum attempts, and others to provide flexibility and control. It employs a while loop to attempt the write operation continuously and waits for its completion. In case of concurrent exceptions, it increments the attempt counter and calculates the sleep time using an exponential backoff strategy before retrying the operation. This approach ensures that the write operation is eventually successful, providing reliability and efficiency in handling concurrent operations on Delta tables. Explore the code below to understand its workings and integrate it into your projects to enhance concurrent operations handling.

+
from datetime import datetime
+from time import sleep
+from delta.exceptions import (
+    ConcurrentAppendException,
+    ConcurrentDeleteReadException,
+    ConcurrentDeleteDeleteException,
+)
+import math
+
+
+def streaming_write_with_concurrent_retry(
+    stream, max_attempts=3, indefinite=False, table=None, path=None
+):
+    """
+    Handles concurrent write operations to a Delta table or path by retrying the operation
+    in case of specific concurrent exceptions.
+
+    :param stream: The data stream to be written.
+    :param max_attempts: The maximum number of retry attempts. Default is 3.
+    :param indefinite: If True, will keep retrying indefinitely. Default is False.
+    :param table: The Delta table to write to.
+    :param path: The path to write to.
+    :return: The result of writer.awaitTermination().
+    """
+
+    attempt = 0  # Initialize attempt counter
+
+    while True:
+        try:
+            # Choose the writer based on whether table or path is provided
+            if table:
+                writer = stream.table(table)
+            elif path:
+                writer = stream.start(path)
+            else:
+                writer = stream.start()
+
+            # Attempt to write and wait for termination
+            return writer.awaitTermination()
+
+        # Handle concurrent exceptions
+        except (
+            ConcurrentAppendException,
+            ConcurrentDeleteReadException,
+            ConcurrentDeleteDeleteException,
+        ) as e:
+
+            # Increment attempt counter
+            attempt += 1
+
+            # If indefinite is False and attempts have reached max_attempts, raise the exception
+            if not indefinite and attempt >= max_attempts:
+                raise e from None
+
+            # Calculate sleep time using exponential backoff strategy
+            sleep_time = min(120, math.pow(2, attempt))
+
+            # Sleep for the calculated time before retrying
+            sleep(sleep_time)
+
+

Solution 4: Row-Level Concurrency (Advanced Feature)?

+
    +
  • +

    Reduces conflicts between concurrent write operations by detecting changes at the row level.

    +
  • +
  • +

    Automatically resolves competing changes in concurrent writes that update or delete different rows in the same data file.

    +
  • +
+
+

Available only on Delta tables with deletion vectors enabled and on Photon-enabled compute running Databricks Runtime 14.0 and above.

+
+

Reference

+

Isolation levels and write conflicts on Databricks +*Learn about the isolation levels and potential conflicts when performing concurrent transactions on tables on…*docs.databricks.com

+

Thank You for Reading!

+

I hope you found this article helpful and informative. If you enjoyed this post, please consider giving it a clap 👏 and sharing it with your network. Your support is greatly appreciated!

+

**CanadianDataGuy**

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/2023-12-03-apnabanale/index.html b/public/blog/2023-12-03-apnabanale/index.html new file mode 100644 index 000000000..50468d1f6 --- /dev/null +++ b/public/blog/2023-12-03-apnabanale/index.html @@ -0,0 +1,805 @@ + + + + + + + + + +Apna bana le + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Apna bana le

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + December 3, 2023 + +

+ + +
+

Solving Delta Table Concurrency Issues

+

Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

+

Why Do These Issues Happen?

+
    +
  • +

    Concurrent Append Exception (ConcurrentAppendException):** +This error happens when another operation is adding files to the same section (or any section in a table without partitions) that your operation is reading from. These file additions can happen due to INSERT, DELETE, UPDATE, or MERGE operations. By default, with the WriteSerializable isolation level, adding files without checking any data (known as blind INSERT operations) won’t cause any issues with any operation, even if they are working on the same section (or any section in a table without partitions). However, if the isolation level is changed to Serializable, then these blind additions may cause conflicts. This error is commonly seen during simultaneous DELETE, UPDATE, or MERGE operations. Even though these operations might be updating different sections, a conflict can occur if one operation is reading the same section that another operation is updating at the same time.

    +
  • +
  • +

    Concurrent Delete Read Exception: +It occurs when a transaction is trying to read a file that is being deleted by another transaction. This is to ensure that a transaction does not read data that is in the process of being deleted.

    +
  • +
  • +

    Concurrent Delete Delete Exception: +— Occurs when two transactions are trying to delete the same file. +— Delta Lake ensures that a file is not deleted more than once.

    +
  • +
+

Understanding Isolation Levels: Serializable vs. WriteSerializable

+

Isolation levels in a database control how much transactions are protected from each other’s changes. Delta Lake on Databricks offers two such levels: Serializable and WriteSerializable.

+

1. Serializable: +— This is the highest level of isolation. +— It ensures that all write and read operations are done in a specific order, just like how they appear in the table’s history. +— This means operations are carried out one by one, maintaining the order and ensuring the final result is as expected.

+

2. WriteSerializable (Default): +— This level is a bit more relaxed compared to Serializable. +— It guarantees order only for write operations, not for reads. +— Even though it’s more relaxed, it’s still more strict than the Snapshot isolation level. +— This level is used by default as it offers a good mix of data consistency and availability for most operations.

+

Solution 1: Setting the Isolation Level:

+
    +
  • +

    Use the ALTER TABLE command to set the isolation level to Serializable or WriteSerializable.

    +

    ALTER TABLE SET TBLPROPERTIES (‘delta.isolationLevel’ = ‘WriteSerializable’)

    +
  • +
+

Solution 2: Avoiding Conflicts Using Partitioning and Disjoint Command Conditions

+

When working with tables, sometimes two operations can clash or conflict, especially if they are working on the same set of files. This can cause problems and errors. But, there’s a way to avoid this! You can organize or partition your table based on certain columns that are often used in operations. This way, different operations work on different parts of the table, preventing them from clashing.

+

For example, imagine two commands — one is updating the table for dates after January 1, 2010, and another is deleting from the table for dates before January 1, 2010. These two can clash if the table is not organized by date, as both might try to change the same files. But if you partition the table by date, these operations won’t conflict, making things smooth and error-free.

+

However, be careful while choosing the column for partitioning. If you choose a column that has a lot of unique values, it can create a large number of subdirectories. This can lead to other issues, affecting the performance of operations on the table.

+

By using these strategies and understanding the insights from Databricks regarding isolation levels, row-level concurrency, and write conflicts, you can make your Delta operations more robust, reliable, and efficient.

+

Solution 3: Code block with exponential retry

+

The Python code below offers a robust solution to address this challenge. It is designed to manage concurrent write operations to a Delta table or path by intelligently retrying the operation in the event of specific concurrent exceptions. Streaming_write_with_concurrent_retry takes parameters such as the data stream, maximum attempts, and others to provide flexibility and control. It employs a while loop to attempt the write operation continuously and waits for its completion. In case of concurrent exceptions, it increments the attempt counter and calculates the sleep time using an exponential backoff strategy before retrying the operation. This approach ensures that the write operation is eventually successful, providing reliability and efficiency in handling concurrent operations on Delta tables. Explore the code below to understand its workings and integrate it into your projects to enhance concurrent operations handling.

+
from datetime import datetime
+from time import sleep
+from delta.exceptions import (
+    ConcurrentAppendException,
+    ConcurrentDeleteReadException,
+    ConcurrentDeleteDeleteException,
+)
+import math
+
+
+def streaming_write_with_concurrent_retry(
+    stream, max_attempts=3, indefinite=False, table=None, path=None
+):
+    """
+    Handles concurrent write operations to a Delta table or path by retrying the operation
+    in case of specific concurrent exceptions.
+
+    :param stream: The data stream to be written.
+    :param max_attempts: The maximum number of retry attempts. Default is 3.
+    :param indefinite: If True, will keep retrying indefinitely. Default is False.
+    :param table: The Delta table to write to.
+    :param path: The path to write to.
+    :return: The result of writer.awaitTermination().
+    """
+
+    attempt = 0  # Initialize attempt counter
+
+    while True:
+        try:
+            # Choose the writer based on whether table or path is provided
+            if table:
+                writer = stream.table(table)
+            elif path:
+                writer = stream.start(path)
+            else:
+                writer = stream.start()
+
+            # Attempt to write and wait for termination
+            return writer.awaitTermination()
+
+        # Handle concurrent exceptions
+        except (
+            ConcurrentAppendException,
+            ConcurrentDeleteReadException,
+            ConcurrentDeleteDeleteException,
+        ) as e:
+
+            # Increment attempt counter
+            attempt += 1
+
+            # If indefinite is False and attempts have reached max_attempts, raise the exception
+            if not indefinite and attempt >= max_attempts:
+                raise e from None
+
+            # Calculate sleep time using exponential backoff strategy
+            sleep_time = min(120, math.pow(2, attempt))
+
+            # Sleep for the calculated time before retrying
+            sleep(sleep_time)
+
+

Solution 4: Row-Level Concurrency (Advanced Feature)?

+
    +
  • +

    Reduces conflicts between concurrent write operations by detecting changes at the row level.

    +
  • +
  • +

    Automatically resolves competing changes in concurrent writes that update or delete different rows in the same data file.

    +
  • +
+
+

Available only on Delta tables with deletion vectors enabled and on Photon-enabled compute running Databricks Runtime 14.0 and above.

+
+

Reference

+

Isolation levels and write conflicts on Databricks +*Learn about the isolation levels and potential conflicts when performing concurrent transactions on tables on…*docs.databricks.com

+

Thank You for Reading!

+

I hope you found this article helpful and informative. If you enjoyed this post, please consider giving it a clap 👏 and sharing it with your network. Your support is greatly appreciated!

+

**CanadianDataGuy**

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/arcresources/index.html b/public/blog/arcresources/index.html new file mode 100644 index 000000000..52f7f26eb --- /dev/null +++ b/public/blog/arcresources/index.html @@ -0,0 +1,689 @@ + + + + + + + + + +ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + May 24, 2022 + +

+ + +
+

ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. +In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/audantic/index.html b/public/blog/audantic/index.html new file mode 100644 index 000000000..1af29a92b --- /dev/null +++ b/public/blog/audantic/index.html @@ -0,0 +1,690 @@ + + + + + + + + + +How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + May 5, 2022 + +

+ + +
+

To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. We were able to achieve a 73% reduction in the time it took our pipeline to run as well as saving 21% on the cost of the run.

+

https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/index.html b/public/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/index.html new file mode 100644 index 000000000..d11ff7a72 --- /dev/null +++ b/public/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/index.html @@ -0,0 +1,766 @@ + + + + + + + + + +Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + February 23, 2023 + +

+ + +
+

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

+

Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot.

+

Here are some basic rules for using Databricks Workspace:

+
    +
  1. +

    Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. Use a consistent naming convention and create a logical hierarchy of folders.

    +
  2. +
  3. +

    Use Databricks jobs for every scheduled job: If it’s a scheduled job, then it should run on a job Cluster. It’s cheaper to run on a job cluster over an interactive cluster.

    +
  4. +
  5. +

    Service principals should own all production jobs so those permissions stay intact whenever individuals leave the team or company. Ensure the principals have access to the underlying notebooks for this to work correctly.

    +
  6. +
  7. +

    For jobs with multiple tasks, task values should be used so that parameters must be set at the beginning of a job. Typically, we’ve had the first task of a multi-task workflow put the parameters into task values so that all other tasks can pull accordingly as needed. Make sure to set the default and debugValues so that individual notebook-level testing can still take place.

    +
  8. +
  9. +

    Use Databricks CLI/API: Use Databricks CLI to automate repetitive tasks, such as uploading and downloading notebooks and managing clusters.

    +
  10. +
  11. +

    Use secrets management: Use Databricks secrets management to store and manage sensitive information, such as API keys and database credentials.

    +
  12. +
  13. +

    Optimize cluster usage: Use autoscaling to scale your clusters up or down based on workload automatically. Use autoscaling and right-sizing to optimize your cluster usage and reduce costs.

    +
  14. +
  15. +

    Monitor cluster performance: Monitor your cluster performance using Databricks metrics and logs to identify performance bottlenecks and optimize your workload.

    +
  16. +
  17. +

    Use Databricks Delta: Use Databricks Delta for data management, which provides reliability, performance, and scalability for your data. Delta helps you manage your data using ACID transactions, schema enforcement, and data versioning.

    +
  18. +
  19. +

    Use Databricks MLflow: Use Databricks MLflow to track and manage your machine learning experiments. MLflow helps you manage your experiments, track model performance, and deploy models to production.

    +
  20. +
  21. +

    A single logical top-level construct: is an E2 master account (AWS) or a subscription object (Azure Databricks/GCP). In AWS, provision a single E2 account per organization that provides a unified pane of visibility and control to all workspaces. In this way, your admin activity is centralized, with the ability to enable SSO, Audit Logs, and Unity Catalog.

    +
  22. +
  23. +

    Adopt Unity Catalog for data governance. Enabling cross-cloud and cross-workspace analytics brings a new level of governance and control to the Lakehouse.

    +
  24. +
+

Photo by Marco Bicca on Unsplash

+

Once you have multiple teams using the same workspace, it’s time to set more controls.

+

Here are examples of some Advanced best practices to put in place:

+
    +
  1. +

    Cost Control by establishing boundaries on compute: Cloud can become expensive fast if no limits are set. A cluster policy is a tool that limits a user or group’s cluster creation permissions based on a set of policy rules.

    +
  2. +
  3. +

    Tag everything: Keep an eye on your usage and know the Databricks Resource Limits; if your workspace usage or user count starts to grow, consider adopting a more involved workspace organization strategy to avoid per-workspace limits. Leverage resource tagging wherever possible to track cost and usage metrics.

    +
  4. +
  5. +

    Publish a shared central repository of code examples which are unique to your business. Example: How to connect to your on-prem SQL Server, Oracle, Salesforce, etc.

    +
  6. +
  7. +

    Automate your cloud processes. This ranges from every aspect of your infrastructure, including SSO/SCIM, Infrastructure-as-Code with a tool such as Terraform, CI/CD pipelines and Repos, cloud backup, and monitoring (using both cloud-native and third-party tools).

    +
  8. +
  9. +

    Enable audit logging at the account level. Having auditability from the very start of your lakehouse journey allows you to establish a historical baseline. Often, you only realize how much you need audit logs when you really, really need them.

    +
  10. +
  11. +

    Setup Account Level Identities should be enabled as this allows for centralized principal management for all workspaces, thereby simplifying administration. We recommend setting up features like SSO, SCIM and Audit Logs at the account level. Workspace-level SSO is still required, until the SSO Federation feature is available.

    +
  12. +
  13. +

    Tracking the ongoing consumption for all workload types across all workspaces is visible to account admins via the accounts console. We recommend setting up billable usage log delivery so that it all goes to your central cloud storage for chargeback and analysis. Budget API (In Preview) should be configured at the account level, which allows account administrators to create thresholds at the workspaces, SKU, and cluster tags level and receives alerts on consumption so that timely action can be taken to remain within allotted budgets. Use a tool such as Overwatch to track usage at an even more granular level to help identify areas of improvement when it comes to utilizing computing resources.

    +
  14. +
+

Reference:

+

5 Best Practices for Databricks Workspaces +Databricks Workspace Administration - Best Practices for Account, Workspace and Metastore Admins

+

https://www.databricks.com/blog/2022/10/18/best-practices-cost-management-databricks.html +Databricks Workspace Administration - Best Practices for Account, Workspace and Metastore Admins +Best Practices for Cost Management on Databricks +Serving Up a Primer for Unity Catalog Onboarding

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ + +
+ + +
+ + + +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/deltalivetablesadvancedqa/index.html b/public/blog/deltalivetablesadvancedqa/index.html new file mode 100644 index 000000000..00a1d1afe --- /dev/null +++ b/public/blog/deltalivetablesadvancedqa/index.html @@ -0,0 +1,831 @@ + + + + + + + + + +Delta Live Tables Advanced Q &amp; A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Delta Live Tables Advanced Q & A

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + March 3, 2023 + +

+ + +
+

Delta Live Tables Advanced Q & A

+

This is primarily written for those trying to handle edge cases.

+

Photo by Joshua Earle on Unsplash

+

Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data?

+

The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

+

What is the gotcha here?

+

The data has lost its natural ordering which is fine in most cases, meaning it did not go into the Delta table in the same order it was generated. This is in contrast to an ideal world in which Kafka had infinite retention, and a single DLT pipeline would have ingested the data.

+

If and only if you are using the table as a Streaming source with Watermarking downstream then while reading this data, we will have to instruct Spark Streaming to sort the data while reading it. We can do this by using the following parameter ‘withEventTimeOrder’.

+
spark.readStream.format("delta")
+        .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}")
+        .option("withEventTimeOrder", "true")
+        .table(f"{schema_name}.{table_name}")
+
+

You can further read about this solution here https://canadiandataguy.medium.com/how-to-write-your-first-spark-application-with-stream-stream-joins-with-working-code-dd9b0b39f814#d828

+

To reiterate, the gotcha only applies if you use this table as a Streaming Source along with Watermarking.

+

Q2.) How do I handle deletes in a Streaming Table?

+

Let’s take GDPR as an example of where we need to enforce retention on the Delta table. One can run a regular DELETE command on the table and then in the DLT pipeline make changes to downstream consumers.

+
+

“By default, streaming tables require append-only sources. When a streaming table uses another streaming table as a source, and the source streaming table requires updates or deletes, for example, GDPR “right to be forgotten” processing, the skipChangeCommits flag can be set on the target streaming table to ignore those changes. For more information about this flag, see Ignore updates and deletes.”

+
+
@table
+def b():
+   return spark.readStream.option("skipChangeCommits", "true").table("LIVE.A")
+
+

https://docs.databricks.com/en/delta-live-tables/python-ref.html#configure-a-streaming-table-to-ignore-changes-in-a-source-streaming-table

+

Q3.) How to enable mergeSchema on DLT table?

+

This is already handled in DLT. If you want to control otherwise explicitly, you can pass the following spark conf property at the DLT pipeline or table level.

+
+

spark.databricks.delta.schema.autoMerge.enabled True

+
+

If you are using Autoloader, consider playing with different schema evolution modes while reading data.

+

.option(“cloudFiles.schemaEvolutionMode”, “addNewColumns”)

+

Q4.) How to change the location where the table is stored?

+
    +
  1. +

    Manually copy the data using Deep Clone with the {new_location}

    +
  2. +
  3. +

    Create a new DLT pipeline and set the path = {new_location}

    +

    @dlt.table( +name="", +comment="", +spark_conf={"" : “<value”, “<key” : “”}, +table_properties={"" : “”, “” : “”}, +path="", +partition_cols=["", “”], +schema=“schema-definition”, +temporary=False)

    +
  4. +
  5. +

    In your DLT pipeline configuration, set this property pipelines.tableManagedByMultiplePipelinesCheck.enabledto false

    +
  6. +
  7. +

    Now, we need to make sure that we do not read any duplicate data because we cannot reuse our old checkpoint. We will solve this by using filters or providing a starting configuration for the streaming source. E.g., if your streaming source is:

    +
  8. +
+

4. a) Kafka: Then we will provide offset information. More information can be found here.

+

4. b) Delta:

+

For example, suppose you have a table user_events. If you want to read changes since version 5, use:

+
spark.readStream.format("delta")
+  .option("startingVersion", "5")
+  .load("/tmp/delta/user_events")
+
+

If you want to read changes since 2023–03–03, use:

+
spark.readStream.format("delta")
+  .option("startingTimestamp", "2018-10-18")
+  .load("/tmp/delta/user_events")
+
+

More details can be found [here](http://For example, suppose you have a table user_events. If you want to read changes since version 5, use: Scala Copy to clipboardCopy spark.readStream.format(“delta”) .option(“startingVersion”, “5”) .load("/tmp/delta/user_events") If you want to read changes since 2018-10-18, use: Scala Copy to clipboardCopy spark.readStream.format(“delta”) .option(“startingTimestamp”, “2018-10-18”) .load("/tmp/delta/user_events")).

+
    +
  1. To do step 4, you should parameterize your DLT pipeline, which can be done by following these instructions.
  2. +
+

Q5.) Does DLT support Identity Columns?

+

Yes, more details here. However, Identity columns are not supported with APPLY CHANGES tables.

+

Q6.) How to stream out of a table which was loaded using apply_changes?

+

This is generally not recommended. The target of the APPLY CHANGES INTO query or apply_changes the function cannot be used as a source for a streaming live table. A table that reads from the target of a APPLY CHANGES INTO query or apply_changes function must be a live table.

+

You can rely on enabling SCD and then use audit columns (__START_AT &__END_AT)to identify the changes. However, the downstream would still have to do a batch read and filter on these audit columns to limit the information being read.

+

If you are adventurous and still want to do a read stream of this source. You need to enableChangeDataFeed on the delta table ‘fact_sales’.

+
@dlt.table(name="fact_sales",
+  comment="This is a fact tables for sales",
+  partition_cols = ["order_date"],
+  table_properties={
+    "pipelines.autoOptimize.zOrderCols": "StoreId,ItemId",
+    "delta.enableChangeDataFeed": "true",
+  }
+)
+
+

Then you can decide to stream changes out of the _apply_changes{table_name} . Make sure to handle tombstones/deletes as part of your downstream pipeline.

+

Q7.) How to delete Data using DLT?

+

Use the Change Data Capture functionality of DLT. The particular expression which will help you achieve this is called **apply_as_deletes. **You can change the parameter to match your custom criteria. For example, if you had bad records originating in a specific time interval or file name, you can change the expression to meet your custom criteria.

+
import dlt
+from pyspark.sql.functions import col, expr
+
+@dlt.view
+def users():
+  return spark.readStream.format("delta").table("cdc_data.users")
+
+dlt.create_streaming_live_table("target")
+
+dlt.apply_changes(
+  target = "target",
+  source = "users",
+  keys = ["userId"],
+  sequence_by = col("sequenceNum"),
+  apply_as_deletes = expr("operation = 'DELETE' or {any other custom logic} "),
+  except_column_list = ["operation", "sequenceNum"],
+  stored_as_scd_type = "2"
+)
+
+

Q8.) How to avoid accidental overwrites in DLT?

+

Set this property so that tables cannot be overwritten.

+

pipelines.reset.allowed false

+

Q9.) DLT Pipeline was deleted, but the Delta table exists. What to do now? What if the owner has left the org and I need a new DLT pipeline to take care of the table

+

Step 1.) Verify via CLI if the pipeline has been deleted

+
databricks --profile <your_env> pipelines list
+databricks --profile <your_env> pipelines get --pipeline-id <deleted_pipeline_id>
+
+

Step 2.) Change the owner of the table

+
ALTER TABLE <db>.<table> SET TBLPROPERTIES(pipelines.pipelineId = '<NEW_PIPELINE_ID>');
+
+

Note: In case you do not have a pipeline ID yet, you can use the below parameter once; run your pipeline to get the pipeline ID and then remove the below parameter.

+

pipelines.tableManagedByMultiplePipelinesCheck.enabledto false

+

Q10.) How does sequence_by work in apply_changes() ?

+

There are two types of data management strategies with apply_changes:

+

Type 1 involves keeping only the latest state of a record. This means that if an older record arrives out-of-order and we already have a newer record in the target, the older record will not update the target because it is not the latest state.

+

Type 2 involves keeping a history of all records. This means that if an out-of-order record arrives, it is considered as a historical entry and will update the table by adding a new entry to the history.

+

If you experience unexpected behavior with deletes; you should consider altering the following property pipelines.cdc.tombstoneGCThresholdInSeconds +Delta Live Tables properties reference +*This article provides a reference for Delta Live Tables JSON setting specification and table properties in Databricks.*docs.databricks.com

+

Q11.) How to make Python UDF work in DLT + UC?

+

Add a tag to your pipeline “PythonUDF.enabled”: “true” and DLT “channel”: “PREVIEW”

+
        {
+            "label": "default",
+            "aws_attributes": {
+                "instance_profile_arn": "fill_this"
+            },
+            "custom_tags": {
+                "PythonUDF.enabled": "true"
+            },
+            "autoscale": {
+                "min_workers": 1,
+                "max_workers": 5,
+                "mode": "ENHANCED"
+            }
+        },
+
+

This information is subject to change, and the parameter might not be needed in the future.

+

Footnote:

+

Thank you for taking the time to read this article. If you found it helpful or enjoyable, please consider clapping to show appreciation and help others discover it. Don’t forget to follow me for more insightful content, and visit my website **CanadianDataGuy.com** for additional resources and information. Your support and feedback are essential to me, and I appreciate your engagement with my work.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/index.html b/public/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/index.html new file mode 100644 index 000000000..359c53972 --- /dev/null +++ b/public/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/index.html @@ -0,0 +1,756 @@ + + + + + + + + + +Dive Deep into Spark Streaming Checkpoint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Dive Deep into Spark Streaming Checkpoint

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + March 21, 2023 + +

+ + +
+

From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint

+

Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. To mitigate this problem, Spark provides a mechanism called checkpointing.

+

+

Why do we even need a checkpoint?

+

Someone needs to remember what was done before or what was processed before, or what we know so far. All this information needs to be stored somewhere. The place where this is stored is called a Checkpoint.

+

How does checkpoint work?

+

Think of it as a 3 step process:

+
    +
  1. Fetch the source metadata and write to Write Ahead Log (WAL)/Offsets
  2. +
  3. Fetch the source data, process it, and write to sink
  4. +
  5. Write state & commit information
  6. +
+

Checkpoints store the current offsets and state values (e.g. aggregate values) for your stream. Checkpoints are stream specific, so each should be set to its own location.

+
+

This is an advanced blog and should be read with the expectation of familiarizing and not understanding. Read this and bookmark it; once you come across a situation where you need to dig into the checkpoint, this blog will come in handy.

+
+

What is inside a checkpoint folder?

+

It will have 3 folders inside it and a metadata file:

+
    +
  • +

    offsets: This contains the WAL information.

    +
  • +
  • +

    commits: Once data is processed, the offset information will go inside it

    +
  • +
  • +

    State: Only if stateful operations are involved.

    +
  • +
  • +

    metadata: Metadata about the stream. This is a file

    +
  • +
+

list of files

+

What is inside the Offsets file?

+

The easiest way to think about it is that once we start processing a micro-batch of data. We need to store an upper bound mark and a lower bound mark of the data. This mark could be called an offset. Think if you a measuring something with a scale and you need to log the reading. This reading, aka the offset, we will store in the offsets file.

+

Water Ruller

+

Different sources like Kafka, Kinesis, Delta, etc., all have different ways of defining offsets, but conceptually they are the same.

+
    +
  • Kafka: you will find { “topic_name: {“partition_number”: offset_number } . More information can be found in this blog.
  • +
+

For this blog, let’s concentrate on Delta as a streaming source.

+
    +
  • Reservoir ID (aka Table ID): This is your Delta Table id
  • +
  • reservoirVersion is the version of the Delta table that the micro-batch(current stream execution) started with
  • +
  • Index: File index of the current Delta Table version being processed. Every time you write to a Delta table, the Table version is incremented. As part of the write operation, multiple files are written. Within that Delta Table Version, the file number being processed is represented by the index.
  • +
  • isStartingVersion: This is just true or false. It is true to denote a query starting rather than processing changes.
  • +
+

offsets

+

Metadata

+

This stores the stream-id, which is generated when the stream starts and remains the same throughout the life of the checkpoint.

+

metadata

+

Commits

+

These files are generated only when the micro-batch succeeds. Offsets are generated at the start of the micro-batch. If the offset did not have a corresponding commit, a failure happened when processing that offset.

+

In an ideal scenario, the number of commit files equals the number of offset files. However, when they are not equal, the next Spark Streaming knows where to start because it’s stored in the offset file, which did not have a corresponding commit. Furthermore, watermarking information would be found here.

+

commits

+

State Store

+

This folder only has data in the case of Stateful Streaming, where the State is stored on disk for resiliency purposes. Thus when failures happen, the state can be recovered from here.

+
    +
  • State is also stored as a Delta table
  • +
  • _metadata will hold the schema of the state
  • +
+

state

+
+
+

References

+

Please spare some time to look at the below to help absorb the above content further.

+
    +
  1. https://www.youtube.com/watch?v=1cBDGsSbwRA&t=442s
  2. +
  3. https://www.databricks.com/blog/2022/12/12/streaming-production-collected-best-practices.html
  4. +
+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/index.html b/public/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/index.html new file mode 100644 index 000000000..f5bd44945 --- /dev/null +++ b/public/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/index.html @@ -0,0 +1,803 @@ + + + + + + + + + +How I wrote my first Spark Streaming Application with Joins? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How I wrote my first Spark Streaming Application with Joins?

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + January 25, 2023 + +

+ + +
+

How I wrote my first Spark Streaming Application with Joins with working code

+

When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application.

+

In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

+

Photo by Ian Schneider on Unsplash

+

Scenario:

+

Let’s assume we have a streaming source with data arriving all the time. We want to add more attributes from another table( Think lookup table/ dimension table). Thus we will stream the data and join with the lookup table via Stream-Batch join. The result would be written as a Delta table, which could be used downstream for analytics or streaming.

+

Imports & Parameters

+
from pyspark.sql import functions as F
+from faker import Faker
+import uuid
+
+# define schema name and where should the table be stored
+schema_name = "test_streaming_joins"
+schema_storage_location = "/tmp/CHOOSE_A_PERMANENT_LOCATION/"
+
+
+# Please download this file from https://simplemaps.com/data/us-zips then download and place it at a location of your choice and then change the value for the variable below
+static_table_csv_file = "/FileStore/jitesh.soni/data/us_zip_code_and_its_attributes.csv"
+
+# Static table specification
+static_table_name = "static_zip_codes"
+
+
+# Target Stareaming Table specification
+target_table_name = "joined_datasets"
+
+# Recommend you to keep the checkpoint next to the Delta table so that you do have to notion about where the checkpoint is
+checkpoint_location = f"{schema_storage_location}/{target_table_name}/_checkpoints/"Create Target Database
+
+
    +
  • +

    The below code will help create a schema/database with comments and storage locations for tables

    +

    create_schema_sql = f""" +CREATE SCHEMA IF NOT EXISTS {schema_name} +COMMENT ‘This is {schema_name} schema’ +LOCATION ‘{schema_storage_location}’ +WITH DBPROPERTIES ( Owner=‘Jitesh’); +""" +print(f"create_schema_sql: {create_schema_sql}")

    +
  • +
+

Generate Static Or a lookup Dataset

+

We will use a public dataset source with attributes about a zip code. This could be any other static source or a Delta table being updated in parallel.

+

Note: If you pick a static source and start streaming, Spark Streaming will only read it once. If you have a few updates to the static source, you will have to restart the Spark Stream so it rereads the static source.

+

Meanwhile, if you have the Delta table as a source, then Spark Streaming will identify the update automatically, and nothing extra needs to be done.

+
csv_df = (
+    spark.read.option("header", True)
+    .option("inferSchema", True)
+    .csv(static_table_csv_file)
+)
+display(csv_df)
+csv_df.write.saveAsTable(f"{schema_name}.{static_table_name}")
+
+

Next, we will Z-order the table on the key, which would be used in joins. This will help Spark Streaming do efficient joins because the Delta table is sorted by join key with statistics about which file contains which key value.

+
spark.sql(
+    f"""
+    OPTIMIZE {schema_name}.{static_table_name} ZORDER BY (zip);
+    """
+)
+
+

Generate Streaming Dataset

+

We will generate a Streaming dataset using the Faker library. In the below code, we will define a few user-defined functions.

+
fake = Faker()
+fake_id = F.udf(lambda: str(uuid.uuid4()))
+fake_firstname = F.udf(fake.first_name)
+fake_lastname = F.udf(fake.last_name)
+fake_email = F.udf(fake.ascii_company_email)
+# fake_date = F.udf(lambda:fake.date_time_this_month().strftime("%Y-%m-%d %H:%M:%S"))
+fake_address = F.udf(fake.address)
+fake_zipcode = F.udf(fake.zipcode)
+
+

Now, we will use *spark.readStream.format(“rate”) *to generate data at your desired rate.

+
streaming_df = (
+    spark.readStream.format("rate")
+    .option("numPartitions", 10)
+    .option("rowsPerSecond", 1 * 1000)
+    .load()
+    .withColumn("fake_id", fake_id())
+    .withColumn("fake_firstname", fake_firstname())
+    .withColumn("fake_lastname", fake_lastname())
+    .withColumn("fake_email", fake_email())
+    .withColumn("fake_address", fake_address())
+    .withColumn("fake_zipcode", fake_zipcode())
+)
+
+# You can uncomment the below display command to check if the code in this cell works
+# display(streaming_df)
+
+

Stream- Static Join or Stream -Delta Join

+

Structured Streaming supports joins (inner join and left join) between a streaming and a static DataFrame or a Delta Table. However, a few types of stream-static outer Joins are not supported yet.

+
lookup_delta_df = spark.read.table(static_table_name)
+
+
+joined_streaming_df = streaming_df.join(
+    lookup_delta_df,
+    streaming_df["fake_zipcode"] == lookup_delta_df["zip"],
+    "left_outer",
+).drop("fake_zipcode")
+# display(joined_streaming_df)
+
+

Orchestrate the pipeline and write Spark Stream to Delta Table

+

Some Tips:

+
    +
  • +

    Give your streaming query a name. It’s good because this name will appear on Spark UI and help you monitor the stream.

    +
  • +
  • +

    If you are not planning to run the Stream continuously then use trigger(availableNow=True). This helps process all pending data and then stops the stream automatically.

    +

    ( +joined_streaming_df.writeStream +# .trigger(availableNow=True) +.queryName(“do_a_stream_join_with_the_delta_table”) +.option(“checkpointLocation”, checkpoint_location) +.format(“delta”) +.toTable(f"{schema_name}.{target_table_name}") +)

    +
  • +
+

Download the code

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/howtogetthejobidandrunidforadatabricksjob/index.html b/public/blog/howtogetthejobidandrunidforadatabricksjob/index.html new file mode 100644 index 000000000..b606c93cc --- /dev/null +++ b/public/blog/howtogetthejobidandrunidforadatabricksjob/index.html @@ -0,0 +1,715 @@ + + + + + + + + + +How to get the Job ID and Run ID for a Databricks Job + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to get the Job ID and Run ID for a Databricks Job

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + February 23, 2023 + +

+ + +
+

How to get the Job ID and Run ID for a Databricks Job with working code

+

Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here.

+

This is a simple 2-step process:

+
    +
  1. +

    Pass the parameter when defining the job/task

    +
  2. +
  3. +

    Get/Fetch and print the values

    +
  4. +
+

Step 1: Pass the parameters

+

+

Step 2: Get/Fetch and print the values

+
print(f"""
+  job_id: {dbutils.widgets.get('job_id')}
+  run_id: {dbutils.widgets.get('run_id')}
+  parent_run_id: {dbutils.widgets.get('parent_run_id')}
+  task_key: {dbutils.widgets.get('task_key')}
+  """)
+
+

Next step, when you run the job; you should see an output like this

+

+

Advanced & Optional:

+

In case you do not have a specific attribute in mind but want to capture the whole context information instead.

+
+

The below is code based and attributes are subject to change without notice

+
+
dbutils.notebook.entry_point.getDbutils().notebook().getContext().toJson()
+
+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/index.html b/public/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/index.html new file mode 100644 index 000000000..5a33a0001 --- /dev/null +++ b/public/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/index.html @@ -0,0 +1,862 @@ + + + + + + + + + +How to parameterize Delta Live Tables and import reusable functions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to parameterize Delta Live Tables and import reusable functions

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + December 13, 2022 + +

+ + +
+

How to parameterize Delta Live Tables and import reusable functions with working code

+

Photo by Roberto Nickson on Unsplash

+

This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append().

+
+

Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. However, in case these reusable_functions.py file exists outside the repository, you can take the sys.path.append() approach mentioned below.*

+
+

Overall, this a 4-step process:

+
    +
  1. +

    Create a *reusable_functions.py *file

    +
  2. +
  3. +

    Add code to receive the DLT parameters

    +
  4. +
  5. +

    Append the path to reusable_functions.py file and import the functions in the notebook

    +
  6. +
  7. +

    Create a DLT pipeline and set/pass parameters

    +
  8. +
+

1. Create a *reusable_functions.py *file

+

Create a reusable function in a Python File (not Notebook), so we can import it later. Let’s call the file ‘reusable_functions.py’ below and place it in a path. Please make sure to note the absolute path of the folder where this file will be placed.

+
from pyspark.sql import DataFrame
+from pyspark.sql.functions import current_timestamp, current_date
+
+def append_ingestion_columns(_df: DataFrame):
+    return _df.withColumn("ingestion_timestamp", current_timestamp()).withColumn(
+        "ingestion_date", current_date()
+    )
+
+

2. Add code to receive the DLT parameters

+

The below function is defined with try and except block so that it can work with Notebook as well, where we cannot pass the parameter value from the DLT pipeline

+
from pyspark.sql import SparkSession
+
+spark = SparkSession.getActiveSession()
+
+
+def get_parameter_or_return_default(
+    parameter_name: str = "pipeline.parameter_blah_blah",
+    default_value: str = "default_value",
+) -> str:
+    try:
+        parameter = spark.conf.get(parameter_name)
+    except Exception:
+        parameter = default_value
+    return parameter
+
+

In this example, we will pass two parameters: path_to_reusable_functions & *parameter_abc. *Then we will use the function defined previously to get and set default values for both.

+
path_to_reusable_functions = get_parameter_or_return_default(
+    parameter_name="pipeline.path_to_reusable_functions",
+    default_value="/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/",
+)
+
+parameter_abc = get_parameter_or_return_default(
+    parameter_name="pipeline.parameter_abc", default_value="random_default_value"
+)
+
+

3. Append the path to reusable_functions.py file and import the functions in the notebook

+
import sys
+
+# Add the path so functions could be imported
+sys.path.append(path_to_reusable_functions)
+
+# Attempt the import
+from reusable_functions import append_ingestion_columns
+
+

Next step, we will define a function to return a DataFrame and the run display command to see the output of the function. This helps one test if the code works without running the DLT pipeline.

+
def static_dataframe():
+    df_which_we_got_back_after_running_sql = spark.sql(
+        f"""
+            SELECT 
+                '{path_to_reusable_functions}' as path_to_reusable_functions
+                ,'{parameter_abc}' as parameter_abc
+        """
+    )
+    return append_ingestion_columns(df_which_we_got_back_after_running_sql)
+
+
+display(static_dataframe())
+
+

At this point, you should be able to run your notebook and validate everything works before we create a DLT pipeline.

+

Next step, we define a DLT table.

+
import dlt
+
+
+@dlt.table(name="static_table", comment="Static table")
+def dlt_static_table():
+    return static_dataframe()
+
+

4. Create a DLT pipeline and set/pass parameters

+

At this step, we can create a DLT pipeline via UI, add our custom parameters, and assign them values.

+

+

The full JSON representation would look something like this, we only care about the ***configuration ***section in this JSON.

+
{
+    "id": "d40fa97a-5b5e-4fe7-9760-b67d78a724a1",
+    "clusters": [
+        {
+            "label": "default",
+            "policy_id": "E06216CAA0000360",
+            "autoscale": {
+                "min_workers": 1,
+                "max_workers": 2,
+                "mode": "ENHANCED"
+            }
+        },
+        {
+            "label": "maintenance",
+            "policy_id": "E06216CAA0000360"
+        }
+    ],
+    "development": true,
+    "continuous": false,
+    "channel": "PREVIEW",
+    "edition": "CORE",
+    "photon": false,
+    "libraries": [
+        {
+            "notebook": {
+                "path": "/Repos/jitesh.soni@databricks.com/material_for_public_consumption/notebooks/how_to_parameterize_delta_live_tables_and_import_reusable_functions"
+            }
+        }
+    ],
+    "name": "how_to_parameterize_delta_live_tables_and_import_reusable_functions",
+    "storage": "dbfs:/pipelines/d40fa97a-5b5e-4fe7-9760-b67d78a724a1",
+    "configuration": {
+        "pipeline.parameter_abc": "this_was_passed_from_dlt_config",
+        "pipeline.path_to_reusable_functions": "/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/"
+    },
+    "target": "tmp_taget_schema"
+}
+
+

Trigger your DLT pipeline.

+

If you have reached so far, you should have an end-to-end DLT pipeline working with parameter passing and imports.

+

*Update | How do you edit these parameters via API or CLI

+

Below are screenshots of how to edit these parameters via CLI. The API solution would be similar.

+

Create a JSON file with the parameters:

+
{
+  "id": "d40fa97a-5b5e-4fe7-9760-b67d78a724a1",
+  "name": "how_to_parameterize_delta_live_tables_and_import_reusable_functions",
+  "clusters": [
+        {
+            "label": "default",
+            "policy_id": "E06216CAA0000360",
+            "autoscale": {
+                "min_workers": 1,
+                "max_workers": 5,
+                "mode": "ENHANCED"
+            }
+        },
+        {
+            "label": "maintenance",
+            "policy_id": "E06216CAA0000360"
+        }
+    ],
+"configuration": {
+    "pipeline.parameter_created_from_jobs_cli": "this_was_created_from_jobs_cli",
+    "pipeline.parameter_abc": "this_was_passed_from_dlt_config_via_job_cli",
+    "pipeline.path_to_reusable_functions": "/Workspace/Repos/jitesh.soni@databricks.com/material_for_public_consumption/"
+  },
+  "libraries": [
+        {
+            "notebook": {
+                "path": "/Repos/jitesh.soni@databricks.com/material_for_public_consumption/notebooks/how_to_parameterize_delta_live_tables_and_import_reusable_functions"
+            }
+        }
+    ]
+}
+
+

Call the Datarbricks CLI to push the changes:

+

+

Go back to Delta Live Tables UI and the change would have gone through

+

+

Download the code

+

DLT notebook and Reusable_function.py

+

References

+

Delta Live Tables settings

+

https://docs.databricks.com/workflows/delta-live-tables/delta-live-tables-cookbook.html#import-python-modules-from-a-databricks-repo

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/howtoprepareyourselftobebetteratdatainterviews/index.html b/public/blog/howtoprepareyourselftobebetteratdatainterviews/index.html new file mode 100644 index 000000000..c4818b5f8 --- /dev/null +++ b/public/blog/howtoprepareyourselftobebetteratdatainterviews/index.html @@ -0,0 +1,742 @@ + + + + + + + + + +How to prepare yourself to be better at Data Interviews? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to prepare yourself to be better at Data Interviews?

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + January 27, 2023 + +

+ + +
+

How to prepare yourself to be better at Data Interviews?

+

In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table.

+

Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

+

Big Data fundamentals and Data Warehousing

+ +

When you read this book, do not expect to understand this after the first attempt; Just read through it. Some chapters make sense in the second or third iteration. Do not quit on the book because it’s too daunting at first. I have read this book at least five times and keep revising some chapters occasionally, improving my conceptual understanding.

+ +

Written by “Ralph Kimball” who is considered a thought leader for Data Warehousing. The concepts you will learn in this book still hold true in 2023.

+

+

I encourage you to ponder how these concepts hold in the Big Data space when you read this book. As a function of time, many of us graduated directly into the Big Data space without understanding data warehousing enough.

+

Think in SQL:

+

Most businesses do a SQL interview in addition to a coding interview because it is a crucial competence for data engineers, scientists or analysts. Building dependable and scalable data processing and data modelling solutions is your job, and SQL should come naturally to you.

+
    +
  • +

    People have a bad habit of running code every time they make a change. However, this often limits one’s ability to think about what big blocks of code do together. A good rule of thumb is to run each SQL solution, at most twice, when practicing for the coding round.

    +
  • +
  • +

    Write formatted SQL with self-descriptive variable names

    +
  • +
  • +

    Learn about partitions, indexes, etc.

    +
  • +
  • +

    (Advanced) Learn about explain plan and how databases run your query

    +
  • +
+

Practice Python/ ( Or a language of your choice)

+

To practice your coding skills, you must use a whiteboard rather than just paper or integrated development environments (IDEs), which provide syntax support and standard formatting. Doing this will make you feel more at ease during the actual coding interview rounds. You ought to be knowledgeable about both simple and complicated issues. Learning the foundations of a programming language, like Python, and practicing its syntax and commands are good places to start.

+
    +
  • +

    Learn the Big O Notion so that you can write performant and testable code

    +
  • +
  • +

    Practice on an online platform: InterviewQuery, Leetcode, Hackerrank, etc

    +
  • +
+

Read the below books

+ +

Practice Interviewing:

+

As you start applying for jobs and come close to the on-site interview stage, practicing mock interviews with peers and experienced coaches who can give you personalized feedback to improve your interview performance is beneficial. Interviewing is a skill that needs to be polished before you go to the on-site interviews.

+

I recommend the candidates think about opportunity loss. Finding a good coach who can provide valuable tips can shorten your learning curve and will help you identify your blind spot.

+

Example: If you land a job worth $100K a week early. Then a week represents ~$1923 of opportunity.

+

Blind:

+

**Blind** is an anonymous community app for the workplace. The tech community is very helpful and transparent. Always research the company, their interview style and compensation before your interviews.

+
+

“Opinions expressed are solely my own and do not express the views or opinions of my employer.”

+
+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/index.html b/public/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/index.html new file mode 100644 index 000000000..4da06778b --- /dev/null +++ b/public/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/index.html @@ -0,0 +1,775 @@ + + + + + + + + + +How to upgrade your Spark Stream application with a new checkpoint! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to upgrade your Spark Stream application with a new checkpoint!

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + January 25, 2023 + +

+ + +
+

How to upgrade your Spark Stream application with a new checkpoint With working code

+

Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios:

+
    +
  1. +

    You are doing a code/application change where you are changing logic

    +
  2. +
  3. +

    Major Spark Version upgrade from Spark 2.x to Spark 3.x

    +
  4. +
  5. +

    The previous deployment was wrong, and you want to reprocess from a certain point

    +
  6. +
+

There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

+

Not every scenario requires a new checkpoint. Here is a list of things you can change without requiring a new checkpoint.

+

This blog helps you understand how to handle a scenario where a new checkpoint is unavoidable.

+

Photo by Patrick Tomasso on Unsplash

+

Kafka Basics: Topics, partition & offset

+

**Kafka Cluster has Topics: **Topics are a way ****to organize messages. Each topic has a name that is unique across the entire Kafka cluster. Messages are sent to and read from specific topics. In other words, producers write data on a topic, and consumers read data from the topic.

+

Topics have **Partitions, **and data/messages are distributed across partitions. Every message belongs to a single partition.

+

Partition has messages, each with a unique sequential identifier within the partition called the Offset.

+

What is the takeaway here?

+

We must identify what offset has already been processed for each partition, and this information can be found inside the checkpoint.

+

+

What information is inside the checkpoint?

+
    +
  • +

    Fetch metadata & write it to WAL(write-ahead log) in the checkpoint. WAL: a roll-forward journal that records transactions that have been committed but not yet applied to the main data

    +
  • +
  • +

    Fetch the actual data → process data with state info and then write it to the sink

    +
  • +
  • +

    Write the stateful information & commit to the checkpoint

    +
  • +
+

Under the checkpoint folder, there are four subfolders:

+
    +
  1. +

    Sources (contain starting offset of Kafka)

    +
  2. +
  3. +

    Offsets (consist of WAL information)

    +
  4. +
  5. +

    Commits (after completion of the entire process, it goes to the commit)

    +
  6. +
  7. +

    State (only for stateful operations + 1 file of metadata)

    +
  8. +
+

How to fetch information about Offset & Partition from the Checkpoint folder?

+

List the files at the checkpoint location; we are looking for the offsets folder.

+
checkpoint_location= "/checkpoint_location/checkpoint_for_kafka_to_delta"
+dbutils.fs.ls(checkpoint_location)dbutils.fs.ls(f”{checkpoint_location}/”)
+
+

+

Next, we will list the files under the commits folder and identify the most recent commits.

+
dbutils.fs.ls(checkpoint_location)
+dbutils.fs.ls(f”{checkpoint_location}/commits”)
+
+/checkpoint_location/checkpoint_for_kafka_to_delta/commits/0
+/checkpoint_location/checkpoint_for_kafka_to_delta/commits/1
+/checkpoint_location/checkpoint_for_kafka_to_delta/commits/2
+
+

Once we identify the last commits file number; we will open the equivalent offsets file. In this example, we can see the latest commits is “2”.

+

Now let’s view the contents of the offsets file.

+
#%fs head {FILL_THE_EXACT_PATH_OF_THE_FILE_WHICH_NEEDS_TO_BE_VIEWED}
+%fs head /checkpoint_location/checkpoint_for_kafka_to_delta/offsets/2
+
+{"batchWatermarkMs":0,"batchTimestampMs":1674623173851,"conf":{"spark.sql.streaming.stateStore.providerClass":"org.apache.spark.sql.execution.streaming.state.HDFSBackedStateStoreProvider","spark.sql.streaming.join.stateFormatVersion":"2","spark.sql.streaming.stateStore.compression.codec":"lz4","spark.sql.streaming.stateStore.rocksdb.formatVersion":"5","spark.sql.streaming.statefulOperator.useStrictDistribution":"true","spark.sql.streaming.flatMapGroupsWithState.stateFormatVersion":"2","spark.sql.streaming.multipleWatermarkPolicy":"min","spark.sql.streaming.aggregation.stateFormatVersion":"2","spark.sql.shuffle.partitions":"200"}}
+{"topic_name_from_kafka":{"0":400000, "1":300000}}
+
+

The information of interest is in the end. This has the topic name and offset per partition.

+

{“topic_name_from_kafka”:{“0”:400000, “1”:300000}}

+

Now the easy part: Use Spark to start reading Kafka from a particular Offset

+

Spark Streaming starts read stream by default with the *latest *offset. However, it provides a parameter “startingOffsets” to select a custom starting point.

+
startingOffsets = """{"topic_name_from_kafka":{"0":400000, "1":300000}}"""
+
+kafka_stream = (spark.readStream
+  .format("kafka")
+  .option("kafka.bootstrap.servers", kafka_bootstrap_servers_plaintext ) 
+  .option("subscribe", topic )
+  .option("startingOffsets", startingOffsets )
+  .load())
+
+display(kafka_stream)
+
+

And we are Done!!. Recommend parameterizing your code so that “startingOffsets” can be passed as a parameter.

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/index.html b/public/blog/index.html new file mode 100644 index 000000000..bc5b4acb6 --- /dev/null +++ b/public/blog/index.html @@ -0,0 +1,1354 @@ + + + + + + + + + +Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Blog

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Solving Delta Table Concurrency Issues

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 30, 2023 +

+ +
+ +

Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 15, 2023 +

+ +
+ +

Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 12, 2023 +

+ +
+ +

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ June 6, 2023 +

+ +
+ +

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

A Productive Life: How to Parallelize Code Execution in Python

+
+

+ + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 23, 2023 +

+ +
+ +

A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to Cut Your Data Processing Costs by 30% with Graviton

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 23, 2023 +

+ +
+ +

How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

+
+

+ + + + in + + best practices, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 19, 2023 +

+ +
+ +

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to write your first Spark application with Stream-Stream Joins with working code.

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 23, 2023 +

+ +
+ +

How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Dive Deep into Spark Streaming Checkpoint

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 21, 2023 +

+ +
+ +

From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Delta Live Tables Advanced Q & A

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 3, 2023 +

+ +
+ +

Delta Live Tables Advanced Q & A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/index.xml b/public/blog/index.xml new file mode 100644 index 000000000..4e5ab7ffa --- /dev/null +++ b/public/blog/index.xml @@ -0,0 +1,190 @@ + + + + Blog on Canadian Data Guy + https://canadiandataguy.com/blog/ + Recent content in Blog on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You&rsquo;re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. + + + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Tue, 12 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + A Productive Life: How to Parallelize Code Execution in Python + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can. + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. + + + How to get the Job ID and Run ID for a Databricks Job + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f&quot;&quot;&quot; job_id: {dbutils. + + + How to prepare yourself to be better at Data Interviews? + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python &amp; Big Data fundamentals. Here is how to prepare for each of them. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + 1 on 1 Interview Coaching + https://canadiandataguy.com/blog/1-on-1-coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/1-on-1-coaching/ + Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks &amp; Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + + Youtube + https://canadiandataguy.com/blog/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/youtube/ + + + + diff --git a/public/blog/mergemultiplesparkstreamsintoadeltatable/index.html b/public/blog/mergemultiplesparkstreamsintoadeltatable/index.html new file mode 100644 index 000000000..79541ac4a --- /dev/null +++ b/public/blog/mergemultiplesparkstreamsintoadeltatable/index.html @@ -0,0 +1,853 @@ + + + + + + + + + +Merge Multiple Spark Streams Into A Delta Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Merge Multiple Spark Streams Into A Delta Table

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + October 13, 2022 + +

+ + +
+

Merge Multiple Spark Streams Into A Delta Table with working code

+

This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table.

+

Overall, the process works in the following manner:

+
    +
  1. +

    Read data from a streaming source

    +
  2. +
  3. +

    Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

    +
  4. +
  5. +

    Our user-defined function runs the Merge and *Optimize over *the target Delta table.

    +
  6. +
+

Architecture

+

+

First, we need some input data to merge. You could technically make a stream out of Kafka, Kinesis, s3, etc.

+

However, for simplicity we will use .format(’rate’) to generate a stream. Feel free to alter numPartitions & rowsPerSecond . These parameters help you control how much volume of data you want to generate. In the below code, we generated 1,000 rows per second across 100 partitions.

+

For the purpose of this blog, we will build 2 Spark streams one for each country Canada & USA.

+

USA’s stream

+
generated_streaming_usa_df = (
+     spark.readStream
+        .format("rate")
+        .option("numPartitions", 100)
+        .option("rowsPerSecond", 1 * 1000)
+        .load()
+        .selectExpr(
+          "md5( CAST (value AS STRING) ) as md5"
+          ,"value"
+          ,"value%1000000 as hash"
+          ,"'USA' AS country"
+          ,"current_timestamp() as ingestion_timestamp"  
+        )
+)
+
+#display(generated_streaming_usa_df)
+
+

Canada’s Stream

+
generated_streaming_canada_df = (
+     spark.readStream
+        .format("rate")
+        .option("numPartitions", 100)
+        .option("rowsPerSecond", 1 * 1000)
+        .load()
+        .selectExpr(
+          "md5( CAST (value AS STRING) ) as md5"
+          ,"value"
+          ,"value%1000000 as hash"
+          ,"'Canada' AS country"
+          ,"current_timestamp() as ingestion_timestamp"    
+        )
+)
+
+#display(generated_streaming_canada_df)
+
+

Parameters / Variables (Feel free to change as per your needs)

+
target_table_name = "to_be_merged_into_table_partitioned_by_country"
+check_point_location_for_usa_stream = f"/tmp/delta/{target_table_name}/_checkpoints/_usa/"
+check_point_location_for_canada_stream = f"/tmp/delta/{target_table_name}/_checkpoints/_canada/"
+join_column_name ="hash"
+partition_column = "country"
+
+

Create an Empty Delta table so data could be merged into it

+
#spark.sql(f"""  DROP TABLE IF EXISTS {target_table_name};""")
+(  
+  generated_steaming_usa_df.writeStream
+  .partitionBy(partition_column)
+  .format("delta")
+  .outputMode("append").trigger(once=True)
+  .option("checkpointLocation", check_point_location_for_usa_stream)
+  .toTable(target_table_name)
+)
+
+

Check if data is populated. If you do not see any data, just run the above code snippet once more. Sometimes it takes time for the data to show up.

+
display(spark.read.table(target_table_name))
+
+

Now we will build the code for the user-defined function which does all the data processing, merge & Optimize

+
def make_changes_using_the_micro_batch(microBatchOutputDF, batchId: int):
+    print(f"Processing batchId: {batchId}")
+    microBatchOutputDF.createOrReplaceTempView("updates")
+    spark_session_for_this_micro_batch = microBatchOutputDF._jdf.sparkSession()
+    spark_session_for_this_micro_batch.sql(f"""
+      SELECT * 
+      FROM (
+        select *
+          ,rank() over(partition by {join_column_name} order by value desc) as dedupe
+        from updates
+        )
+      WHERE 
+          dedupe =1 
+   """).drop("dedupe").createOrReplaceTempView("updates_which_need_to_be_merged")
+    spark_session_for_this_micro_batch.sql(f"""
+    MERGE INTO {target_table_name} target
+    using updates_which_need_to_be_merged u 
+    on u.{partition_column} = target.{partition_column} 
+        AND u.{join_column_name} = target.{join_column_name} 
+    WHEN MATCHED THEN UPDATE SET *
+    WHEN NOT MATCHED THEN INSERT *
+    """)
+    optimize_every_n_batches = 20
+    #Define how often should optimize run? for example: at 50, it means that we will run the optimize command every 50 batches of stream data
+    if batchId % optimize_every_n_batches == 0:
+        optimize_and_zorder_table(table_name = target_table_name,  zorder_by_col_name = join_column_name)
+
+

Optimize/ Z-order a Delta table

+

Why do we need to optimize a table? If we keep adding files to our Delta table and never optimize/sort them then over time we need to read a lot of files during merge time. Thus, optimizing the Delta table after every N merges is better. N needs to be decided on your latency requirements. You could start with N as 10 and change it as per your needs.

+

The below code will run an optimize and zorder command on a given table that is being fed by a stream. Optimize commands can’t run in a silo because it will require us to pause and then resume the stream. Therefore, we need to call this function a part of the upsert function. This enables us to optimize before the next batch of streaming data comes through.

+
from timeit import default_timer as timer
+ 
+ 
+def optimize_and_zorder_table(table_name: str, zorder_by_col_name: str) -> None:
+    """
+    Parameters:
+         table_name: str
+                 name of the table to be optimized
+         zorder_by_col_name: str
+                 comma separated list of columns to zorder by. example "col_a, col_b, col_c"
+    """
+    start = timer()
+    print(f"Met condition to optimize table {table_name}")
+    sql_query_optimize = f"OPTIMIZE  {table_name} ZORDER BY ({zorder_by_col_name})"
+    spark.sql(sql_query_optimize)
+    end = timer()
+    time_elapsed_seconds = end - start
+    print(
+        f"Successfully optimized table {table_name} . Total time elapsed: {time_elapsed_seconds} seconds"
+    )
+
+

Orchestrate the streaming pipeline end to end

+

Read the Canada stream and merge into Delta table

+
(
+  generated_steaming_canada_df
+ .writeStream.format('delta')
+ #.trigger(availableNow=True) 
+ .trigger(processingTime='10 seconds')
+ .option("checkpointLocation", check_point_location_for_canada_stream)
+ .foreachBatch(make_changes_using_the_micro_batch)
+ .start()
+)
+
+

Read the USA stream and merge into Delta table

+
(
+  generated_steaming_usa_df
+ .writeStream.format('delta')
+ .trigger(processingTime='10 seconds')
+ .option("checkpointLocation", check_point_location_for_usa_stream)
+ .foreachBatch(make_changes_using_the_micro_batch)
+ .start()
+)
+
+

Now, let’s validate that data is being populated

+
display(
+    spark.sql(f"""
+        SELECT 
+            {partition_column} as partition_column
+            ,count(1) as row_count
+        FROM 
+            {target_table_name}
+        GROUP BY 
+            {partition_column}
+        """)
+)
+
+

If you have reached so far, you should have an end-to-end pipeline working with streaming data and merging data into a Delta table.

+

Download this notebook

+

https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/Merge%20Multiple%20Spark%20Streams%20Into%20A%20Delta%20Table.py

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/page/1/index.html b/public/blog/page/1/index.html new file mode 100644 index 000000000..4ae51cb36 --- /dev/null +++ b/public/blog/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/blog/ + + + + + + diff --git a/public/blog/page/2/index.html b/public/blog/page/2/index.html new file mode 100644 index 000000000..5bb9b8765 --- /dev/null +++ b/public/blog/page/2/index.html @@ -0,0 +1,1368 @@ + + + + + + + + + +Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Blog

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

+
+

+ + + + in + + best practices, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ February 23, 2023 +

+ +
+ +

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to get the Job ID and Run ID for a Databricks Job

+
+

+ + + + in + + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ February 23, 2023 +

+ +
+ +

How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f""" job_id: {dbutils.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to prepare yourself to be better at Data Interviews?

+
+

+ + + + in + + interviewing, + coaching + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 27, 2023 +

+ +
+ +

How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How I wrote my first Spark Streaming Application with Joins?

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to upgrade your Spark Stream application with a new checkpoint!

+
+

+ + + + in + + streaming, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to parameterize Delta Live Tables and import reusable functions

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ December 13, 2022 +

+ +
+ +

How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Merge Multiple Spark Streams Into A Delta Table

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 13, 2022 +

+ +
+ +

Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 12, 2022 +

+ +
+ +

Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

+
+

+ + + + in + + customer stories + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ May 24, 2022 +

+ +
+ +

ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

+
+

+ + + + in + + customer stories, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ May 5, 2022 +

+ +
+ +

To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/page/3/index.html b/public/blog/page/3/index.html new file mode 100644 index 000000000..01053a495 --- /dev/null +++ b/public/blog/page/3/index.html @@ -0,0 +1,732 @@ + + + + + + + + + +Blog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Blog

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

1 on 1 Interview Coaching

+
+

+ + + + in + + coaching + + + +

+ +
+ +

Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Youtube

+
+

+ + + + in + + coaching + + + +

+ +
+ +

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html b/public/blog/solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html new file mode 100644 index 000000000..6c1b21449 --- /dev/null +++ b/public/blog/solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/index.html @@ -0,0 +1,801 @@ + + + + + + + + + +Solving Delta Table Concurrency Issues: Practical Code Solutions &amp; Insights + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Solving Delta Table Concurrency Issues: Practical Code Solutions & Insights

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + April 23, 2023 + +

+ + +
+

Solving Delta Table Concurrency Issues: Practical Code Solutions & Insights

+

Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

+

Why Do These Issues Happen?

+
    +
  • +

    Concurrent Append Exception (ConcurrentAppendException):** +This error happens when another operation is adding files to the same section (or any section in a table without partitions) that your operation is reading from. These file additions can happen due to INSERT, DELETE, UPDATE, or MERGE operations. By default, with the WriteSerializable isolation level, adding files without checking any data (known as blind INSERT operations) won’t cause any issues with any operation, even if they are working on the same section (or any section in a table without partitions). However, if the isolation level is changed to Serializable, then these blind additions may cause conflicts. This error is commonly seen during simultaneous DELETE, UPDATE, or MERGE operations. Even though these operations might be updating different sections, a conflict can occur if one operation is reading the same section that another operation is updating at the same time.

    +
  • +
  • +

    Concurrent Delete Read Exception: +It occurs when a transaction is trying to read a file that is being deleted by another transaction. This is to ensure that a transaction does not read data that is in the process of being deleted.

    +
  • +
  • +

    Concurrent Delete Delete Exception: +— Occurs when two transactions are trying to delete the same file. +— Delta Lake ensures that a file is not deleted more than once.

    +
  • +
+

Understanding Isolation Levels: Serializable vs. WriteSerializable

+

Isolation levels in a database control how much transactions are protected from each other’s changes. Delta Lake on Databricks offers two such levels: Serializable and WriteSerializable.

+

1. Serializable: +— This is the highest level of isolation. +— It ensures that all write and read operations are done in a specific order, just like how they appear in the table’s history. +— This means operations are carried out one by one, maintaining the order and ensuring the final result is as expected.

+

2. WriteSerializable (Default): +— This level is a bit more relaxed compared to Serializable. +— It guarantees order only for write operations, not for reads. +— Even though it’s more relaxed, it’s still more strict than the Snapshot isolation level. +— This level is used by default as it offers a good mix of data consistency and availability for most operations.

+

Solution 1: Setting the Isolation Level:

+
    +
  • +

    Use the ALTER TABLE command to set the isolation level to Serializable or WriteSerializable.

    +

    ALTER TABLE SET TBLPROPERTIES (‘delta.isolationLevel’ = ‘WriteSerializable’)

    +
  • +
+

Solution 2: Avoiding Conflicts Using Partitioning and Disjoint Command Conditions

+

When working with tables, sometimes two operations can clash or conflict, especially if they are working on the same set of files. This can cause problems and errors. But, there’s a way to avoid this! You can organize or partition your table based on certain columns that are often used in operations. This way, different operations work on different parts of the table, preventing them from clashing.

+

For example, imagine two commands — one is updating the table for dates after January 1, 2010, and another is deleting from the table for dates before January 1, 2010. These two can clash if the table is not organized by date, as both might try to change the same files. But if you partition the table by date, these operations won’t conflict, making things smooth and error-free.

+

However, be careful while choosing the column for partitioning. If you choose a column that has a lot of unique values, it can create a large number of subdirectories. This can lead to other issues, affecting the performance of operations on the table.

+

By using these strategies and understanding the insights from Databricks regarding isolation levels, row-level concurrency, and write conflicts, you can make your Delta operations more robust, reliable, and efficient.

+

Solution 3: Code block with exponential retry

+

The Python code below offers a robust solution to address this challenge. It is designed to manage concurrent write operations to a Delta table or path by intelligently retrying the operation in the event of specific concurrent exceptions. Streaming_write_with_concurrent_retry takes parameters such as the data stream, maximum attempts, and others to provide flexibility and control. It employs a while loop to attempt the write operation continuously and waits for its completion. In case of concurrent exceptions, it increments the attempt counter and calculates the sleep time using an exponential backoff strategy before retrying the operation. This approach ensures that the write operation is eventually successful, providing reliability and efficiency in handling concurrent operations on Delta tables. Explore the code below to understand its workings and integrate it into your projects to enhance concurrent operations handling.

+
from datetime import datetime
+from time import sleep
+from delta.exceptions import (
+    ConcurrentAppendException,
+    ConcurrentDeleteReadException,
+    ConcurrentDeleteDeleteException,
+)
+import math
+
+
+def streaming_write_with_concurrent_retry(
+    stream, max_attempts=3, indefinite=False, table=None, path=None
+):
+    """
+    Handles concurrent write operations to a Delta table or path by retrying the operation
+    in case of specific concurrent exceptions.
+
+    :param stream: The data stream to be written.
+    :param max_attempts: The maximum number of retry attempts. Default is 3.
+    :param indefinite: If True, will keep retrying indefinitely. Default is False.
+    :param table: The Delta table to write to.
+    :param path: The path to write to.
+    :return: The result of writer.awaitTermination().
+    """
+
+    attempt = 0  # Initialize attempt counter
+
+    while True:
+        try:
+            # Choose the writer based on whether table or path is provided
+            if table:
+                writer = stream.table(table)
+            elif path:
+                writer = stream.start(path)
+            else:
+                writer = stream.start()
+
+            # Attempt to write and wait for termination
+            return writer.awaitTermination()
+
+        # Handle concurrent exceptions
+        except (
+            ConcurrentAppendException,
+            ConcurrentDeleteReadException,
+            ConcurrentDeleteDeleteException,
+        ) as e:
+
+            # Increment attempt counter
+            attempt += 1
+
+            # If indefinite is False and attempts have reached max_attempts, raise the exception
+            if not indefinite and attempt >= max_attempts:
+                raise e from None
+
+            # Calculate sleep time using exponential backoff strategy
+            sleep_time = min(120, math.pow(2, attempt))
+
+            # Sleep for the calculated time before retrying
+            sleep(sleep_time)
+
+

Solution 4: Row-Level Concurrency (Advanced Feature)?

+
    +
  • +

    Reduces conflicts between concurrent write operations by detecting changes at the row level.

    +
  • +
  • +

    Automatically resolves competing changes in concurrent writes that update or delete different rows in the same data file.

    +
  • +
+
+

Available only on Delta tables with deletion vectors enabled and on Photon-enabled compute running Databricks Runtime 14.0 and above.

+
+

Reference

+

Isolation levels and write conflicts on Databricks +*Learn about the isolation levels and potential conflicts when performing concurrent transactions on tables on…*docs.databricks.com

+

Thank You for Reading!

+

I hope you found this article helpful and informative. If you enjoyed this post, please consider giving it a clap 👏 and sharing it with your network. Your support is greatly appreciated!

+

**CanadianDataGuy**

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Toronto, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/spark-stream-stream-join/index.html b/public/blog/spark-stream-stream-join/index.html new file mode 100644 index 000000000..aaeca606a --- /dev/null +++ b/public/blog/spark-stream-stream-join/index.html @@ -0,0 +1,1135 @@ + + + + + + + + + +How to write your first Spark application with Stream-Stream Joins with working code. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

How to write your first Spark application with Stream-Stream Joins with working code.

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + March 23, 2023 + +

+ + +
+

How to write your first Spark application with Stream-Stream Joins with working code.

+

Have you been waiting to try Streaming but cannot take the plunge?

+

In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline.

+

The steps involved:

+
    +
  1. Create a fake dataset at scale
  2. +
  3. Set a baseline using traditional SQL
  4. +
  5. Define Temporary Streaming Views
  6. +
  7. Inner Joins with optional Watermarking
  8. +
  9. Left Joins with Watermarking
  10. +
  11. The cold start edge case: withEventTimeOrder
  12. +
  13. Cleanup
  14. +
+

+

What is Stream-Stream Join?

+

Stream-stream join is a widely used operation in stream processing where two or more data streams are joined based on some common attributes or keys. It is essential in several use cases, such as real-time analytics, fraud detection, and IoT data processing.

+

Concept of Stream-Stream Join

+

Stream-stream join combines two or more streams based on a common attribute or key. The join operation is performed on an ongoing basis, with each new data item from the stream triggering a join operation. In stream-stream join, each data item in the stream is treated as an event, and it is matched with the corresponding event from the other stream based on matching criteria. This matching criterion could be a common attribute or key in both streams.

+

When it comes to joining data streams, there are a few key challenges that must be addressed to ensure successful results. One of the biggest hurdles is the fact that, at any given moment, neither stream has a complete view of the dataset. This can make it difficult to find matches between inputs and generate accurate join results.

+

To overcome this challenge, it’s important to buffer past input as a streaming state for both input streams. This allows for every future input to be matched with past input, which can help to generate more accurate join results. Additionally, this buffering process can help to automatically handle late or out-of-order data, which can be common in streaming environments.

+

To further optimize the join process, it’s also important to use watermarks to limit the state. This can help to ensure that only the most relevant data is being used to generate join results, which can help to improve accuracy and reduce processing times.

+

Types of Stream-Stream Join

+

Depending on the nature of the join and the matching criteria, there are several types of stream-stream join operations. Some of the popular types of stream-stream join are:

+

Inner Join +In inner join, only those events are returned where there is a match in both the input streams. This type of join is useful when combining the data from two streams with a common key or attribute.

+

Outer Join +In outer join, all events from both the input streams are included in the joined stream, whether or not there is a match between them. This type of join is useful when we need to combine data from two streams, and there may be missing or incomplete data in either stream.

+

Left Join +In left join, all events from the left input stream are included in the joined stream, and only the matching events from the right input stream are included. This type of join is useful when we need to combine data from two streams and keep all the data from the left stream, even if there is no matching data in the right stream.

+

1. The Setup: Create a fake dataset at scale

+

Most people do not have 2 streams just hanging around for one to experiment with Stream Steam Joins. Thus I used Faker to mock 2 different streams which we will use for this example.

+

The name of the library being used is Faker and faker_vehicle to create Datasets.

+
!pip install faker_vehicle
+!pip install faker
+
+

Imports

+
from faker import Faker
+from faker_vehicle import VehicleProvider
+from pyspark.sql import functions as F
+import uuid
+from utils import logger
+
+

Parameters

+
# define schema name and where should the table be stored
+schema_name = “test_streaming_joins”
+schema_storage_location = “/tmp/CHOOSE_A_PERMANENT_LOCATION/”
+
+

Create the Target Schema/Database +Create a Schema and set location. This way, all tables would inherit the base location.

+
create_schema_sql = f”””
+ CREATE SCHEMA IF NOT EXISTS {schema_name}
+ COMMENT ‘This is {schema_name} schema’
+ LOCATION ‘{schema_storage_location}’
+ WITH DBPROPERTIES ( Owner=’Jitesh’);
+ “””
+print(f”create_schema_sql: {create_schema_sql}”)
+spark.sql(create_schema_sql)
+
+

Use Faker to define functions to help generate fake column values

+
fake = Faker()
+fake.add_provider(VehicleProvider)
+
+event_id = F.udf(lambda: str(uuid.uuid4()))
+vehicle_year_make_model = F.udf(fake.vehicle_year_make_model)
+vehicle_year_make_model_cat = F.udf(fake.vehicle_year_make_model_cat)
+vehicle_make_model = F.udf(fake.vehicle_make_model)
+vehicle_make = F.udf(fake.vehicle_make)
+vehicle_model = F.udf(fake.vehicle_model)
+vehicle_year = F.udf(fake.vehicle_year)
+vehicle_category = F.udf(fake.vehicle_category)
+vehicle_object = F.udf(fake.vehicle_object)
+
+latitude = F.udf(fake.latitude)
+longitude = F.udf(fake.longitude)
+location_on_land = F.udf(fake.location_on_land)
+local_latlng = F.udf(fake.local_latlng)
+zipcode = F.udf(fake.zipcode)
+
+

Generate Streaming source data at your desired rate

+
def generated_vehicle_and_geo_df (rowsPerSecond:int , numPartitions :int ):
+    return (
+        spark.readStream.format("rate")
+        .option("numPartitions", numPartitions)
+        .option("rowsPerSecond", rowsPerSecond)
+        .load()
+        .withColumn("event_id", event_id())
+        .withColumn("vehicle_year_make_model", vehicle_year_make_model())
+        .withColumn("vehicle_year_make_model_cat", vehicle_year_make_model_cat())
+        .withColumn("vehicle_make_model", vehicle_make_model())
+        .withColumn("vehicle_make", vehicle_make())
+        .withColumn("vehicle_year", vehicle_year())
+        .withColumn("vehicle_category", vehicle_category())
+        .withColumn("vehicle_object", vehicle_object())
+        .withColumn("latitude", latitude())
+        .withColumn("longitude", longitude())
+        .withColumn("location_on_land", location_on_land())
+        .withColumn("local_latlng", local_latlng())
+        .withColumn("zipcode", zipcode())
+        )
+
+# You can uncomment the below display command to check if the code in this cell works
+#display(generated_vehicle_and_geo_df)
+
+# You can uncomment the below display command to check if the code in this cell works
+#display(generated_vehicle_and_geo_df)
+
+

Now let’s generate the base source table and let’s call it Vehicle_Geo

+
table_name_vehicle_geo= "vehicle_geo"
+def stream_write_to_vehicle_geo_table(rowsPerSecond: int = 1000, numPartitions: int = 10):
+    
+    (
+        generated_vehicle_and_geo_df(rowsPerSecond, numPartitions)
+            .writeStream
+            .queryName(f"write_to_delta_table: {table_name_vehicle_geo}")
+            .option("checkpointLocation", f"{schema_storage_location}/{table_name_vehicle_geo}/_checkpoint")
+            .format("delta")
+            .toTable(f"{schema_name}.{table_name_vehicle_geo}")
+    )
+stream_write_to_vehicle_geo_table(rowsPerSecond = 1000, numPartitions = 10)
+
+

Let the above code run for a few iterations, and you can play with rowsPerSecond and numPartitions to control how much data you would like to generate. Once you have generated enough data, kill the above stream and get a base line for row count.

+
spark.read.table(f"{schema_name}.{table_name_vehicle_geo}").count()
+
+display(
+    spark.sql(f"""
+    SELECT * 
+    FROM {schema_name}.{table_name_vehicle_geo}
+""")
+)
+
+

Let’s also get a min & max of the timestamp column as we would be leveraging it for watermarking.

+
display(
+    spark.sql(f"""
+    SELECT 
+         min(timestamp)
+        ,max(timestamp)
+        ,current_timestamp()
+    FROM {schema_name}.{table_name_vehicle_geo}
+""")
+)
+
+

Next, we will break this Delta table into 2 different tables

+

Because for Stream-Stream Joins we need 2 different streams. We will use Delta To Delta Streaming here to create these tables.

+
    +
  1. a ) Table: Vehicle
  2. +
+
table_name_vehicle = "vehicle"
+vehicle_df = (
+    spark.readStream.format("delta")
+    .option("maxFilesPerTrigger", "100")
+    .table(f"{schema_name}.vehicle_geo")
+    .selectExpr(
+        "event_id",
+        "timestamp as vehicle_timestamp",
+        "vehicle_year_make_model",
+        "vehicle_year_make_model_cat",
+        "vehicle_make_model",
+        "vehicle_make",
+        "vehicle_year",
+        "vehicle_category",
+        "vehicle_object",
+    )
+)
+
+
+def stream_write_to_vehicle_table():
+
+    (
+        vehicle_df.writeStream
+        # .trigger(availableNow=True)
+        .queryName(f"write_to_delta_table: {table_name_vehicle}")
+        .option(
+            "checkpointLocation",
+            f"{schema_storage_location}/{table_name_vehicle}/_checkpoint",
+        )
+        .format("delta")
+        .toTable(f"{schema_name}.{table_name_vehicle}")
+    )
+
+
+stream_write_to_vehicle_table()
+
    +
  1. b) Table: Geo
  2. +
+

We have added a filter when we write to this table. This would be useful when we emulate the left join scenario. Filter: where(“value like ‘1%’ “)

+
geo_df = (
+    spark.readStream.format("delta").option("maxFilesPerTrigger","100").table(f"{schema_name}.vehicle_geo")
+        .selectExpr(
+            "event_id"
+            ,"value"
+            ,"timestamp as geo_timestamp"
+            ,"latitude"
+            ,"longitude"
+            ,"location_on_land"
+            ,"local_latlng"
+            ,"cast( zipcode as integer) as zipcode"
+        ).where("value like '1%' ") 
+    )
+#geo_df.printSchema()
+#display(geo_df)
+
+table_name_geo = "geo"
+def stream_write_to_geo_table():
+    
+    (   geo_df
+        .writeStream
+        #.trigger(availableNow=True)
+        .queryName(f"write_to_delta_table: {table_name_geo}")
+        .option("checkpointLocation", f"{schema_storage_location}/{table_name_geo}/_checkpoint")
+        .format("delta")
+        .toTable(f"{schema_name}.{table_name_geo}")
+    )
+    
+stream_write_to_geo_table()    
+
+

2. Set a baseline using traditional SQL

+

Before we do the actual streaming joins. Let’s do a regular join and figure out the expected row count.

+

Get row count from Inner Join

+
sql_query_batch_inner_join = f'''
+        SELECT count(vehicle.event_id) as row_count_for_inner_join
+        FROM {schema_name}.{table_name_vehicle} vehicle
+        JOIN {schema_name}.{table_name_geo} geo
+        ON vehicle.event_id = geo.event_id
+    AND vehicle_timestamp >= geo_timestamp  - INTERVAL 5 MINUTES        
+        '''
+print(f''' Run SQL Query: 
+          {sql_query_batch_inner_join}       
+       ''')
+display( spark.sql(sql_query_batch_inner_join) )
+
+

Get row count from Inner Join

+
sql_query_batch_left_join = f'''
+        SELECT count(vehicle.event_id) as row_count_for_left_join
+        FROM {schema_name}.{table_name_vehicle} vehicle
+        LEFT JOIN {schema_name}.{table_name_geo} geo
+        ON vehicle.event_id = geo.event_id
+            -- Assume there is a business logic that timestamp cannot be more than 15 minutes off
+    AND vehicle_timestamp >= geo_timestamp  - INTERVAL 5 MINUTES
+        '''
+print(f''' Run SQL Query: 
+          {sql_query_batch_left_join}       
+       ''')
+display( spark.sql(sql_query_batch_left_join) )
+
+

Summary so far:

+
    +
  1. +

    We created a Source Delta Table: vehicle_geo

    +
  2. +
  3. +

    We took the previous table and divided its column into two tables: Vehicle and Geo

    +
  4. +
  5. +

    Vehicle row count matches with vehicle_geo, and it has a subset of those columns

    +
  6. +
  7. +

    The Geo row count is lesser than Vehicle because we added a filter when we wrote to the Geo table

    +
  8. +
  9. +

    We ran 2 SQL to identify what the row count should be after we do stream-stream join

    +
  10. +
+

3. Define Temporary Streaming Views

+

Some people prefer to write the logic in SQL. Thus, we are creating streaming views which could be manipulated with SQL. The below code block will help create a view and set a watermark on the stream.

+
def stream_from_delta_and_create_view (schema_name: str, table_name:str, column_to_watermark_on:str, how_late_can_the_data_be: str = "2 minutes" , maxFilesPerTrigger: int = 100):
+    view_name = f"_streaming_vw_{schema_name}_{table_name}"
+    print(f"Table {schema_name}.{table_name} is now streaming under a temporoary view called {view_name}")
+    (
+        spark.readStream.format("delta")
+        .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}")
+        .option("withEventTimeOrder", "true")
+        .table(f"{schema_name}.{table_name}")
+        .withWatermark(f"{column_to_watermark_on}",how_late_can_the_data_be)
+        .createOrReplaceTempView(view_name)
+    )
+
+

3. a Create Vehicle Stream

+

Let’s create a Vehicle Stream and set its watermark as 1mins

+
stream_from_delta_and_create_view(schema_name =schema_name, table_name = 'vehicle', column_to_watermark_on ="vehicle_timestamp", how_late_can_the_data_be = "1 minutes" )
+
+

Let’s visualize the stream.

+
display(
+    spark.sql(f'''
+        SELECT *
+        FROM _streaming_vw_test_streaming_joins_vehicle
+    ''')
+)
+
+

You can also do an aggregation on the stream. It’s out of the scope of this blog, but I wanted to show you how you can do it

+
display(
+    spark.sql(f'''
+        SELECT 
+            vehicle_make
+            ,count(1) as row_count
+        FROM _streaming_vw_test_streaming_joins_vehicle
+        GROUP BY vehicle_make
+        ORDER BY vehicle_make
+    ''')
+)
+
+

3. b Create Geo Stream

+

Let’s create a Geo Stream and set its watermark as 2 mins

+
stream_from_delta_and_create_view(schema_name =schema_name, table_name = 'geo', column_to_watermark_on ="geo_timestamp", how_late_can_the_data_be = "2 minutes" )
+
+

Have a look at what the data looks like

+
display(
+    spark.sql(f'''
+        SELECT *
+        FROM _streaming_vw_test_streaming_joins_geo
+    ''')
+)
+
+

4. Inner Joins with optional Watermarking

+

While inner joins on any kind of columns and with any kind of conditions are possible in streaming environments, it’s important to be aware of the potential for unbounded state growth. As new input arrives, it can potentially match with any input from the past, leading to a rapidly increasing streaming state size.

+

To avoid this issue, it’s essential to define additional join conditions that prevent indefinitely old inputs from matching with future inputs. By doing so, it’s possible to clear old inputs from the state, which can help to prevent unbounded state growth and ensure more efficient processing.

+

There are a variety of techniques that can be used to define these additional join conditions. For example, you might limit the scope of the join by only matching on a subset of columns, or you might set a time-based constraint that prevents old inputs from being considered after a certain period of time has elapsed.

+

Ultimately, the key to managing streaming state size and ensuring efficient join processing is to consider the unique requirements of your specific use case carefully and to leverage the right techniques and tools to optimize your join conditions accordingly. Although watermarking could be optional, I would highly recommend you set a watermark on both streams.

+
sql_for_stream_stream_inner_join = f"""
+    SELECT 
+        vehicle.*
+        ,geo.latitude
+        ,geo.longitude
+        ,geo.zipcode
+    FROM _streaming_vw_test_streaming_joins_vehicle vehicle
+    JOIN _streaming_vw_test_streaming_joins_geo geo
+    ON vehicle.event_id = geo.event_id
+    -- Assume there is a business logic that timestamp cannot be more than X minutes off
+    AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 minutes
+"""
+#display(spark.sql(sql_for_stream_stream_inner_join))
+
+table_name_stream_stream_innner_join ='stream_stream_innner_join'
+
+(   spark.sql(sql_for_inner_join)
+    .writeStream
+    #.trigger(availableNow=True)
+        .queryName(f"write_to_delta_table: {table_name_stream_stream_innner_join}")
+        .option("checkpointLocation", f"{schema_storage_location}/{table_name_stream_stream_innner_join}/_checkpoint")
+        .format("delta")
+        .toTable(f"{schema_name}.{table_name_stream_stream_innner_join}")
+)
+
+

If the stream has finished then in the next step. You should find that the row count should match up with the regular batch SQL Job

+
spark.read.table(f"{schema_name}.{table_name_stream_stream_innner_join}").count()
+
+

How was the watermark computed in this scenario?

+

When we defined streaming views for Vehicle and Geo, we set them as 1 min and 2 min, respectively.

+

If you look at the join condition we mentioned :

+
AND vehicle_timestamp >= geo_timestamp - INTERVAL 5 minutes
+
+

5 min + 2 min = 7 min.

+

Spark Streaming would automatically calculate this 7 min number and the state would be cleared after that.

+

5. Left Joins with Watermarking

+

While the watermark + event-time constraints is optional for inner joins, for outer joins they must be specified. This is because for generating the NULL results in outer join, the engine must know when an input row is not going to match with anything in future. Hence, the watermark + event-time constraints must be specified for generating correct results.

+

5.a How Left Joins works differently than an Inner Join

+

One important factor is that the outer NULL results will be generated with a delay that depends on the specified watermark delay and the time range condition. This delay is necessary to ensure that there were no matches, and that there will be no matches in the future.

+

In the current implementation of the micro-batch engine, watermarks are advanced at the end of each micro-batch, and the next micro-batch uses the updated watermark to clean up the state and output outer results. However, this means that the generation of outer results may be delayed if there is no new data being received in the stream. If either of the two input streams being joined does not receive data for a while, the outer output (in both left and right cases) may be delayed.

+
sql_for_stream_stream_left_join = f"""
+    SELECT 
+        vehicle.*
+        ,geo.latitude
+        ,geo.longitude
+        ,geo.zipcode
+    FROM _streaming_vw_test_streaming_joins_vehicle vehicle
+    LEFT JOIN _streaming_vw_test_streaming_joins_geo geo
+    ON vehicle.event_id = geo.event_id
+    AND vehicle_timestamp >= geo_timestamp  - INTERVAL 5 MINUTES
+"""
+#display(spark.sql(sql_for_stream_stream_left_join))
+
+table_name_stream_stream_left_join ='stream_stream_left_join'
+
+(   spark.sql(sql_for_stream_stream_left_join)
+    .writeStream
+    #.trigger(availableNow=True)
+        .queryName(f"write_to_delta_table: {table_name_stream_stream_left_join}")
+        .option("checkpointLocation", f"{schema_storage_location}/{table_name_stream_stream_left_join}/_checkpoint")
+        .format("delta")
+        .toTable(f"{schema_name}.{table_name_stream_stream_left_join}")
+)
+
+

If the stream has finished, then in the next step. You should find that the row count should match up with the regular batch SQL Job.

+
spark.read.table(f"{schema_name}.{table_name_stream_stream_left_join}").count()
+
+
+

**You will find that some records that could not match are not being released, which is expected. **The outer NULL results will be generated with a delay that depends on the specified watermark delay and the time range condition. This is because the engine has to wait for that long to ensure there were no matches and there will be no more matches in future. +Watermark will advance once new data is pushed to it

+
+

Thus let’s generate some more fate data to the base table: **vehicle_geo. **This time we are sending a much lower volume of 10 records per second. Let the below command run for at least one batch and then kill it.

+
stream_write_to_vehicle_geo_table(rowsPerSecond = 10, numPartitions = 10)
+
+

5. b What to observe:

+
    +
  1. +

    Soon you should see the watermark moves ahead and the number of records in ‘Aggregation State’ goes down.

    +
  2. +
  3. +

    If you click on the running stream and click the raw data tab and look for “watermark”. You will see it has advanced

    +
  4. +
  5. +

    Once 0 records per second are being processed, that means your stream has caught up, and now your row count should match up with the traditional SQL left join

    +

    spark.read.table(f”{schema_name}.{table_name_stream_stream_left_join}”).count()

    +
  6. +
+

6. The cold start edge case: withEventTimeOrder

+
+

“When using a Delta table as a stream source, the query first processes all of the data present in the table. The Delta table at this version is called the initial snapshot. By default, the Delta table’s data files are processed based on which file was last modified. However, the last modification time does not necessarily represent the record event time order. +In a stateful streaming query with a defined watermark, processing files by modification time can result in records being processed in the wrong order. This could lead to records dropping as late events by the watermark. +You can avoid the data drop issue by enabling the following option: +withEventTimeOrder: Whether the initial snapshot should be processed with event time order.

+
+

In our scenario, I pushed this inside Step 3 when we created the temporary streaming views.

+
spark.readStream.format("delta")
+        .option("maxFilesPerTrigger", f"{maxFilesPerTrigger}")
+        .option("withEventTimeOrder", "true")
+        .table(f"{schema_name}.{table_name}")
+
+

7. Cleanup

+

Drop all tables in the database and delete all the checkpoints

+
spark.sql(
+    f"""
+    drop schema if exists {schema_name} CASCADE
+"""
+)
+
+
+dbutils.fs.rm(schema_storage_location, True)
+
+

If you have reached so far, you now have a working pipeline and a solid example which you can use going forward.

+

Download the code

+

https://github.com/jiteshsoni/material_for_public_consumption/blob/main/notebooks/spark_stream_stream_join.py

+

References:

+
    +
  1. +

    https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#stream-stream-joins

    +
  2. +
  3. +

    https://youtu.be/hyZU_bw1-ow?t=1181

    +
  4. +
  5. +

    https://www.youtube.com/watch?v=1cBDGsSbwRA&t=1500s

    +
  6. +
  7. +

    https://www.databricks.com/blog/2022/08/22/feature-deep-dive-watermarking-apache-spark-structured-streaming.html

    +
  8. +
  9. +

    https://docs.databricks.com/structured-streaming/delta-lake.html#process-initial-snapshot-without-data-being-dropped

    +
  10. +
+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs and follow.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/index.html b/public/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/index.html new file mode 100644 index 000000000..67ee9f636 --- /dev/null +++ b/public/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/index.html @@ -0,0 +1,806 @@ + + + + + + + + + +Using Spark Streaming to merge/upsert data into a Delta Lake with working code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+
+
+
+
+ + +
+
+ +
+ + + +
+ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + October 12, 2022 + +

+ + +
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+

+

This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source.

+

Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. This function encapsulates the Merge and *Optimize *to the target Delta table.

+

First, we need some input data to merge. You could technically make a stream out of Kafka, Kinesis, s3, etc. for simplicity. Let’s generate a stream using the below. Feel free to alter numPartitions & rowsPerSecond . These parameters help you control how much volume of data you want to generate. In the below code, we generated 10,000 rows per second across 100 partitions.

+

Generate streaming data at your desired rate

+
generated_df = (
+     spark.readStream
+        .format("rate")
+        .option("numPartitions", 100)
+        .option("rowsPerSecond", 10 * 1000)
+        .load()
+        .selectExpr(
+          "md5( CAST (value AS STRING) ) as md5"
+          ,"value"
+          ,"value%1000000 as hash"
+        )
+)
+ 
+#display(generated_df)
+
+

Parameters / Variables (Feel free to change as per your needs)

+
target_table_name = "to_be_merged_into_table"
+check_point_location = f"/tmp/delta/{target_table_name}/_checkpoints/"
+join_column_name ="hash"
+
+

Create an Empty Delta table so data could be merged into it

+
spark.sql(f"""  DROP TABLE IF EXISTS {target_table_name};""")
+(  
+  generated_df.writeStream
+  .format("delta")
+  .outputMode("append").trigger(once=True)
+  .option("checkpointLocation", check_point_location)
+  .toTable(target_table_name)
+)
+
+

Check if data is populated

+
display(spark.read.table(target_table_name))
+
+

A user-defined function which does the data processing, Merge & Optimize

+
def make_changes_using_the_micro_batch(microBatchOutputDF, batchId: int):
+    print(f"Processing batchId: {batchId}")
+    microBatchOutputDF.createOrReplaceTempView("updates")
+    spark_session_for_this_micro_batch = microBatchOutputDF._jdf.sparkSession()
+    spark_session_for_this_micro_batch.sql(f"""
+      SELECT * 
+      FROM (
+        select *
+          ,rank() over(partition by {join_column_name} order by value desc) as dedupe
+        from updates
+        )
+      WHERE 
+          dedupe =1 
+   """).drop("dedupe").createOrReplaceTempView("updates_which_need_to_be_merged")
+    spark_session_for_this_micro_batch.sql(f"""
+    MERGE INTO {target_table_name} target
+    using updates_which_need_to_be_merged u
+    on u.{join_column_name} = target.{join_column_name} 
+    WHEN MATCHED THEN UPDATE SET *
+    WHEN NOT MATCHED THEN INSERT *
+    """)
+    optimize_every_n_batches = 20
+    #Define how often should optimize run? for example: at 50, it means that we will run the optimize command every 50 batches of stream data
+    if batchId % optimize_every_n_batches == 0:
+        optimize_and_zorder_table(table_name = target_table_name,  zorder_by_col_name = join_column_name)
+
+

Optimize/ Z-order a Delta table

+

Why do we need to optimize a table? If we keep adding files to our Delta table and never optimize/sort them then over time we need to read a lot of files during merge time. Thus, optimizing the Delta table after every N merges is better. N needs to be decided on your latency requirements. You could start with N as 10 and change it as per your needs.

+

The below code will run an optimize and zorder command on a given table that is being fed by a stream. Optimize commands can’t run in a silo because it will require us to pause and then resume the stream. Therefore, we need to call this function a part of the upsert function. This enables us to optimize before the next batch of streaming data comes through.

+
from timeit import default_timer as timer
+ 
+ 
+def optimize_and_zorder_table(table_name: str, zorder_by_col_name: str) -> None:
+    """
+    Parameters:
+         table_name: str
+                 name of the table to be optimized
+         zorder_by_col_name: str
+                 comma separated list of columns to zorder by. example "col_a, col_b, col_c"
+    """
+    start = timer()
+    print(f"Met condition to optimize table {table_name}")
+    sql_query_optimize = f"OPTIMIZE  {table_name} ZORDER BY ({zorder_by_col_name})"
+    spark.sql(sql_query_optimize)
+    end = timer()
+    time_elapsed_seconds = end - start
+    print(
+        f"Successfully optimized table {table_name} . Total time elapsed: {time_elapsed_seconds} seconds"
+    )
+
+

Orchestrate from readStream -> Merge -> Optimize

+
(
+  generated_df
+ .writeStream.format('delta')
+ .trigger(processingTime='30 seconds')
+ .option("checkpointLocation", check_point_location)
+ .foreachBatch(make_changes_using_the_micro_batch)
+ .start()
+)
+
+

If you have reached so far, you should have an end-to-end pipeline working with streaming data and merging data into a Delta table.

+

As the next step, let’s use the previous target table as our new streaming source.

+

Use the target table as a source for the next streaming pipeline

+

Change data feed allows Databricks to track row-level changes between versions of a Delta table. When enabled on a Delta table, the runtime records change events for all the data written into the table. This includes the row data along with metadata indicating whether the specified row was inserted, deleted, or updated.

+

Reference: https://docs.databricks.com/delta/delta-change-data-feed.html#use-delta-lake-change-data-feed-on-databricks

+
spark.sql(f'''
+ALTER TABLE {target_table_name} SET TBLPROPERTIES (delta.enableChangeDataFeed=true)
+''')
+
+

Reading change data as a stream

+
display(
+   spark.readStream.format("delta") 
+  .option("readChangeFeed", "true") 
+  .table(target_table_name)
+)
+
+

Download this notebook

+

Spark Streaming Using For Each Batch & Merge.html

+

Footnotes

+

If you’re interested in learning more and keeping up to date with the latest about Spark, Delta, DBT, Python, SQL, Terraform, and other big data technologies, check out my other blogs.

+ +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/blog/youtube/index.html b/public/blog/youtube/index.html new file mode 100644 index 000000000..b3e134f50 --- /dev/null +++ b/public/blog/youtube/index.html @@ -0,0 +1,663 @@ + + + + + + + + + +Youtube + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Youtube

+
+
+
+
+ + +
+
+ +
+ + + +
+ + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/best-practices/index.html b/public/categories/best-practices/index.html new file mode 100644 index 000000000..b6edff225 --- /dev/null +++ b/public/categories/best-practices/index.html @@ -0,0 +1,836 @@ + + + + + + + + + +best practices + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

best practices

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

+
+

+ + + + in + + best practices, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 19, 2023 +

+ +
+ +

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

+
+

+ + + + in + + best practices, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ February 23, 2023 +

+ +
+ +

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/best-practices/index.xml b/public/categories/best-practices/index.xml new file mode 100644 index 000000000..b1b31a009 --- /dev/null +++ b/public/categories/best-practices/index.xml @@ -0,0 +1,28 @@ + + + + best practices on Canadian Data Guy + https://canadiandataguy.com/categories/best-practices/ + Recent content in best practices on Canadian Data Guy + Hugo -- gohugo.io + en-us + Wed, 19 Apr 2023 01:04:45 +0000 + + + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can. + + + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. + + + diff --git a/public/categories/best-practices/page/1/index.html b/public/categories/best-practices/page/1/index.html new file mode 100644 index 000000000..1431510a6 --- /dev/null +++ b/public/categories/best-practices/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/best-practices/ + + + + + + diff --git a/public/categories/coaching/index.html b/public/categories/coaching/index.html new file mode 100644 index 000000000..252c8b043 --- /dev/null +++ b/public/categories/coaching/index.html @@ -0,0 +1,838 @@ + + + + + + + + + +coaching + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

coaching

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

How to prepare yourself to be better at Data Interviews?

+
+

+ + + + in + + interviewing, + coaching + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 27, 2023 +

+ +
+ +

How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

1 on 1 Interview Coaching

+
+

+ + + + in + + coaching + + + +

+ +
+ +

Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Youtube

+
+

+ + + + in + + coaching + + + +

+ +
+ +

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/coaching/index.xml b/public/categories/coaching/index.xml new file mode 100644 index 000000000..8e90483e8 --- /dev/null +++ b/public/categories/coaching/index.xml @@ -0,0 +1,34 @@ + + + + coaching on Canadian Data Guy + https://canadiandataguy.com/categories/coaching/ + Recent content in coaching on Canadian Data Guy + Hugo -- gohugo.io + en-us + Fri, 27 Jan 2023 17:35:21 -0500 + + + How to prepare yourself to be better at Data Interviews? + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python &amp; Big Data fundamentals. Here is how to prepare for each of them. + + + 1 on 1 Interview Coaching + https://canadiandataguy.com/blog/1-on-1-coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/1-on-1-coaching/ + Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks &amp; Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + + Youtube + https://canadiandataguy.com/blog/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/youtube/ + + + + diff --git a/public/categories/coaching/page/1/index.html b/public/categories/coaching/page/1/index.html new file mode 100644 index 000000000..71ed1c952 --- /dev/null +++ b/public/categories/coaching/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/coaching/ + + + + + + diff --git a/public/categories/customer-stories/index.html b/public/categories/customer-stories/index.html new file mode 100644 index 000000000..275110503 --- /dev/null +++ b/public/categories/customer-stories/index.html @@ -0,0 +1,833 @@ + + + + + + + + + +customer stories + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

customer stories

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

+
+

+ + + + in + + customer stories + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ May 24, 2022 +

+ +
+ +

ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

+
+

+ + + + in + + customer stories, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ May 5, 2022 +

+ +
+ +

To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/customer-stories/index.xml b/public/categories/customer-stories/index.xml new file mode 100644 index 000000000..29ccb2202 --- /dev/null +++ b/public/categories/customer-stories/index.xml @@ -0,0 +1,26 @@ + + + + customer stories on Canadian Data Guy + https://canadiandataguy.com/categories/customer-stories/ + Recent content in customer stories on Canadian Data Guy + Hugo -- gohugo.io + en-us + Tue, 24 May 2022 17:35:21 -0500 + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/categories/customer-stories/page/1/index.html b/public/categories/customer-stories/page/1/index.html new file mode 100644 index 000000000..1fad28d0e --- /dev/null +++ b/public/categories/customer-stories/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/customer-stories/ + + + + + + diff --git a/public/categories/databricks/index.html b/public/categories/databricks/index.html new file mode 100644 index 000000000..8ae485ecc --- /dev/null +++ b/public/categories/databricks/index.html @@ -0,0 +1,1397 @@ + + + + + + + + + +databricks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

databricks

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Solving Delta Table Concurrency Issues

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 30, 2023 +

+ +
+ +

Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 15, 2023 +

+ +
+ +

Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 12, 2023 +

+ +
+ +

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ June 6, 2023 +

+ +
+ +

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to Cut Your Data Processing Costs by 30% with Graviton

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 23, 2023 +

+ +
+ +

How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to write your first Spark application with Stream-Stream Joins with working code.

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 23, 2023 +

+ +
+ +

How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Dive Deep into Spark Streaming Checkpoint

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 21, 2023 +

+ +
+ +

From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Delta Live Tables Advanced Q & A

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 3, 2023 +

+ +
+ +

Delta Live Tables Advanced Q & A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

+
+

+ + + + in + + best practices, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ February 23, 2023 +

+ +
+ +

Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to get the Job ID and Run ID for a Databricks Job

+
+

+ + + + in + + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ February 23, 2023 +

+ +
+ +

How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f""" job_id: {dbutils.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/databricks/index.xml b/public/categories/databricks/index.xml new file mode 100644 index 000000000..647a6fa4a --- /dev/null +++ b/public/categories/databricks/index.xml @@ -0,0 +1,134 @@ + + + + databricks on Canadian Data Guy + https://canadiandataguy.com/categories/databricks/ + Recent content in databricks on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You&rsquo;re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. + + + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Tue, 12 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. + + + How to get the Job ID and Run ID for a Databricks Job + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f&quot;&quot;&quot; job_id: {dbutils. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/categories/databricks/page/1/index.html b/public/categories/databricks/page/1/index.html new file mode 100644 index 000000000..11d62a474 --- /dev/null +++ b/public/categories/databricks/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/databricks/ + + + + + + diff --git a/public/categories/databricks/page/2/index.html b/public/categories/databricks/page/2/index.html new file mode 100644 index 000000000..998b0f349 --- /dev/null +++ b/public/categories/databricks/page/2/index.html @@ -0,0 +1,1051 @@ + + + + + + + + + +databricks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

databricks

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

How I wrote my first Spark Streaming Application with Joins?

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to parameterize Delta Live Tables and import reusable functions

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ December 13, 2022 +

+ +
+ +

How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Merge Multiple Spark Streams Into A Delta Table

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 13, 2022 +

+ +
+ +

Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 12, 2022 +

+ +
+ +

Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

+
+

+ + + + in + + customer stories, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ May 5, 2022 +

+ +
+ +

To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/index.html b/public/categories/index.html new file mode 100644 index 000000000..447ee849a --- /dev/null +++ b/public/categories/index.html @@ -0,0 +1,656 @@ + + + + + + + + + +Categories + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

Categories

+
+
+
+
+ + +
+
+
+ + +
+ + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/index.xml b/public/categories/index.xml new file mode 100644 index 000000000..7ecbc9f9f --- /dev/null +++ b/public/categories/index.xml @@ -0,0 +1,68 @@ + + + + Categories on Canadian Data Guy + https://canadiandataguy.com/categories/ + Recent content in Categories on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + databricks + https://canadiandataguy.com/categories/databricks/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/categories/databricks/ + + + + spark + https://canadiandataguy.com/categories/spark/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/categories/spark/ + + + + best practices + https://canadiandataguy.com/categories/best-practices/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/categories/best-practices/ + + + + spark streaming + https://canadiandataguy.com/categories/spark-streaming/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/categories/spark-streaming/ + + + + streaming + https://canadiandataguy.com/categories/streaming/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/categories/streaming/ + + + + coaching + https://canadiandataguy.com/categories/coaching/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/categories/coaching/ + + + + interviewing + https://canadiandataguy.com/categories/interviewing/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/categories/interviewing/ + + + + customer stories + https://canadiandataguy.com/categories/customer-stories/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/categories/customer-stories/ + + + + diff --git a/public/categories/interviewing/index.html b/public/categories/interviewing/index.html new file mode 100644 index 000000000..9cfa54b4d --- /dev/null +++ b/public/categories/interviewing/index.html @@ -0,0 +1,766 @@ + + + + + + + + + +interviewing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

interviewing

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

How to prepare yourself to be better at Data Interviews?

+
+

+ + + + in + + interviewing, + coaching + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 27, 2023 +

+ +
+ +

How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/interviewing/index.xml b/public/categories/interviewing/index.xml new file mode 100644 index 000000000..890517737 --- /dev/null +++ b/public/categories/interviewing/index.xml @@ -0,0 +1,20 @@ + + + + interviewing on Canadian Data Guy + https://canadiandataguy.com/categories/interviewing/ + Recent content in interviewing on Canadian Data Guy + Hugo -- gohugo.io + en-us + Fri, 27 Jan 2023 17:35:21 -0500 + + + How to prepare yourself to be better at Data Interviews? + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python &amp; Big Data fundamentals. Here is how to prepare for each of them. + + + diff --git a/public/categories/interviewing/page/1/index.html b/public/categories/interviewing/page/1/index.html new file mode 100644 index 000000000..160f33dea --- /dev/null +++ b/public/categories/interviewing/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/interviewing/ + + + + + + diff --git a/public/categories/page/1/index.html b/public/categories/page/1/index.html new file mode 100644 index 000000000..bfe79328e --- /dev/null +++ b/public/categories/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/ + + + + + + diff --git a/public/categories/spark-streaming/index.html b/public/categories/spark-streaming/index.html new file mode 100644 index 000000000..b58579449 --- /dev/null +++ b/public/categories/spark-streaming/index.html @@ -0,0 +1,1196 @@ + + + + + + + + + +spark streaming + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

spark streaming

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

+
+

+ + + + in + + best practices, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 19, 2023 +

+ +
+ +

Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Delta Live Tables Advanced Q & A

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 3, 2023 +

+ +
+ +

Delta Live Tables Advanced Q & A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How I wrote my first Spark Streaming Application with Joins?

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to upgrade your Spark Stream application with a new checkpoint!

+
+

+ + + + in + + streaming, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to parameterize Delta Live Tables and import reusable functions

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ December 13, 2022 +

+ +
+ +

How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Merge Multiple Spark Streams Into A Delta Table

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 13, 2022 +

+ +
+ +

Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 12, 2022 +

+ +
+ +

Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/spark-streaming/index.xml b/public/categories/spark-streaming/index.xml new file mode 100644 index 000000000..4ae63ae6a --- /dev/null +++ b/public/categories/spark-streaming/index.xml @@ -0,0 +1,73 @@ + + + + spark streaming on Canadian Data Guy + https://canadiandataguy.com/categories/spark-streaming/ + Recent content in spark streaming on Canadian Data Guy + Hugo -- gohugo.io + en-us + Wed, 19 Apr 2023 01:04:45 +0000 + + + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can. + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/categories/spark-streaming/page/1/index.html b/public/categories/spark-streaming/page/1/index.html new file mode 100644 index 000000000..bf1ba4b2a --- /dev/null +++ b/public/categories/spark-streaming/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/spark-streaming/ + + + + + + diff --git a/public/categories/spark/index.html b/public/categories/spark/index.html new file mode 100644 index 000000000..788c1d6f1 --- /dev/null +++ b/public/categories/spark/index.html @@ -0,0 +1,1043 @@ + + + + + + + + + +spark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

spark

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

Solving Delta Table Concurrency Issues

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 30, 2023 +

+ +
+ +

Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 15, 2023 +

+ +
+ +

Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ September 12, 2023 +

+ +
+ +

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ June 6, 2023 +

+ +
+ +

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to Cut Your Data Processing Costs by 30% with Graviton

+
+

+ + + + in + + databricks, + spark + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ April 23, 2023 +

+ +
+ +

How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/spark/index.xml b/public/categories/spark/index.xml new file mode 100644 index 000000000..e9f6a9945 --- /dev/null +++ b/public/categories/spark/index.xml @@ -0,0 +1,49 @@ + + + + spark on Canadian Data Guy + https://canadiandataguy.com/categories/spark/ + Recent content in spark on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You&rsquo;re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. + + + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Tue, 12 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + diff --git a/public/categories/spark/page/1/index.html b/public/categories/spark/page/1/index.html new file mode 100644 index 000000000..52b68afde --- /dev/null +++ b/public/categories/spark/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/spark/ + + + + + + diff --git a/public/categories/streaming/index.html b/public/categories/streaming/index.html new file mode 100644 index 000000000..707a388fc --- /dev/null +++ b/public/categories/streaming/index.html @@ -0,0 +1,1267 @@ + + + + + + + + + +streaming + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+

streaming

+
+
+
+
+ + +
+
+
+ + +
+ + + +
+
+
+ +
+
+

How to write your first Spark application with Stream-Stream Joins with working code.

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 23, 2023 +

+ +
+ +

How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Dive Deep into Spark Streaming Checkpoint

+
+

+ + + + in + + streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 21, 2023 +

+ +
+ +

From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Delta Live Tables Advanced Q & A

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ March 3, 2023 +

+ +
+ +

Delta Live Tables Advanced Q & A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How I wrote my first Spark Streaming Application with Joins?

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to upgrade your Spark Stream application with a new checkpoint!

+
+

+ + + + in + + streaming, + spark streaming + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ January 25, 2023 +

+ +
+ +

How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

How to parameterize Delta Live Tables and import reusable functions

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ December 13, 2022 +

+ +
+ +

How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Merge Multiple Spark Streams Into A Delta Table

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 13, 2022 +

+ +
+ +

Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

+

Continue reading +

+ +
+
+
+ +
+
+
+ +
+
+

Using Spark Streaming to merge/upsert data into a Delta Lake with working code

+
+

+ + + + in + + streaming, + spark streaming, + databricks + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ October 12, 2022 +

+ +
+ +

Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

+

Continue reading +

+ +
+
+
+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/categories/streaming/index.xml b/public/categories/streaming/index.xml new file mode 100644 index 000000000..493d93448 --- /dev/null +++ b/public/categories/streaming/index.xml @@ -0,0 +1,82 @@ + + + + streaming on Canadian Data Guy + https://canadiandataguy.com/categories/streaming/ + Recent content in streaming on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Mar 2023 06:32:42 +0000 + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/categories/streaming/page/1/index.html b/public/categories/streaming/page/1/index.html new file mode 100644 index 000000000..b904db8fc --- /dev/null +++ b/public/categories/streaming/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/categories/streaming/ + + + + + + diff --git a/public/contact/index.html b/public/contact/index.html new file mode 100644 index 000000000..d7d400e2e --- /dev/null +++ b/public/contact/index.html @@ -0,0 +1,532 @@ + + + + + + + + + +Contact + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+

Contact

+
+
+
+
+ + +
+ + +
+ +
+
+ +
+ +

Do you need Data Expertise?

+

Does your company have a project that requires data engineering, data architecture or streaming expertise? Don’t hesitate to email us. We are happy to answer any questions or requests you may have. We can help with one-off projects or ongoing efforts to advance the shift to a more data-driven culture. Empower leaders and teams better to use data as a tool in the decision-making process.

+ + + + +
+

Contact form

+
+ +
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ + +
+ + +
+
+ + + + + +
+ +
+ +
+ + +
+ +

Address

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + +
+ + + + + + + + + + + + + + +
+ + +
+ + +
+ + + + + + +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/css/animate.css b/public/css/animate.css new file mode 100644 index 000000000..f784ce8f6 --- /dev/null +++ b/public/css/animate.css @@ -0,0 +1,3158 @@ +@charset "UTF-8"; +/*! +Animate.css - http://daneden.me/animate +Licensed under the MIT license - http://opensource.org/licenses/MIT + +Copyright (c) 2014 Daniel Eden +*/ + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.animated.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animated.hinge { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +@-webkit-keyframes bounce { + 0%, 20%, 53%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +@keyframes bounce { + 0%, 20%, 53%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + -ms-transform-origin: center bottom; + transform-origin: center bottom; +} + +@-webkit-keyframes flash { + 0%, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +@keyframes flash { + 0%, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +.flash { + -webkit-animation-name: flash; + animation-name: flash; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes pulse { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes pulse { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.pulse { + -webkit-animation-name: pulse; + animation-name: pulse; +} + +@-webkit-keyframes rubberBand { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes rubberBand { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.rubberBand { + -webkit-animation-name: rubberBand; + animation-name: rubberBand; +} + +@-webkit-keyframes shake { + 0%, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +@keyframes shake { + 0%, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +.shake { + -webkit-animation-name: shake; + animation-name: shake; +} + +@-webkit-keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +@keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +.swing { + -webkit-transform-origin: top center; + -ms-transform-origin: top center; + transform-origin: top center; + -webkit-animation-name: swing; + animation-name: swing; +} + +@-webkit-keyframes tada { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes tada { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.tada { + -webkit-animation-name: tada; + animation-name: tada; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes wobble { + 0% { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes wobble { + 0% { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.wobble { + -webkit-animation-name: wobble; + animation-name: wobble; +} + +@-webkit-keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes bounceIn { + 0%, 20%, 40%, 60%, 80%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.bounceIn { + -webkit-animation-name: bounceIn; + animation-name: bounceIn; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes bounceInDown { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInDown { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInDown { + -webkit-animation-name: bounceInDown; + animation-name: bounceInDown; +} + +@-webkit-keyframes bounceInLeft { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInLeft { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInLeft { + -webkit-animation-name: bounceInLeft; + animation-name: bounceInLeft; +} + +@-webkit-keyframes bounceInRight { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInRight { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInRight { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + +@-webkit-keyframes bounceInUp { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInUp { + 0%, 60%, 75%, 90%, 100% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInUp { + -webkit-animation-name: bounceInUp; + animation-name: bounceInUp; +} + +@-webkit-keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +@keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +.bounceOut { + -webkit-animation-name: bounceOut; + animation-name: bounceOut; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.bounceOutDown { + -webkit-animation-name: bounceOutDown; + animation-name: bounceOutDown; +} + +@-webkit-keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.bounceOutLeft { + -webkit-animation-name: bounceOutLeft; + animation-name: bounceOutLeft; +} + +@-webkit-keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.bounceOutRight { + -webkit-animation-name: bounceOutRight; + animation-name: bounceOutRight; +} + +@-webkit-keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.bounceOutUp { + -webkit-animation-name: bounceOutUp; + animation-name: bounceOutUp; +} + +@-webkit-keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +@keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +.fadeIn { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + +@-webkit-keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; +} + +@-webkit-keyframes fadeInDownBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDownBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDownBig { + -webkit-animation-name: fadeInDownBig; + animation-name: fadeInDownBig; +} + +@-webkit-keyframes fadeInLeft { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeft { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeft { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; +} + +@-webkit-keyframes fadeInLeftBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeftBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeftBig { + -webkit-animation-name: fadeInLeftBig; + animation-name: fadeInLeftBig; +} + +@-webkit-keyframes fadeInRight { + 0% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRight { + 0% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRight { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +@-webkit-keyframes fadeInRightBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRightBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRightBig { + -webkit-animation-name: fadeInRightBig; + animation-name: fadeInRightBig; +} + +@-webkit-keyframes fadeInUp { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUp { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUp { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +@-webkit-keyframes fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUpBig { + -webkit-animation-name: fadeInUpBig; + animation-name: fadeInUpBig; +} + +@-webkit-keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} + +@keyframes fadeOut { + 0% {opacity: 1;} + 100% {opacity: 0;} +} + +.fadeOut { + -webkit-animation-name: fadeOut; + animation-name: fadeOut; +} + +@-webkit-keyframes fadeOutDown { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes fadeOutDown { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.fadeOutDown { + -webkit-animation-name: fadeOutDown; + animation-name: fadeOutDown; +} + +@-webkit-keyframes fadeOutDownBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes fadeOutDownBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.fadeOutDownBig { + -webkit-animation-name: fadeOutDownBig; + animation-name: fadeOutDownBig; +} + +@-webkit-keyframes fadeOutLeft { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes fadeOutLeft { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.fadeOutLeft { + -webkit-animation-name: fadeOutLeft; + animation-name: fadeOutLeft; +} + +@-webkit-keyframes fadeOutLeftBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes fadeOutLeftBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.fadeOutLeftBig { + -webkit-animation-name: fadeOutLeftBig; + animation-name: fadeOutLeftBig; +} + +@-webkit-keyframes fadeOutRight { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes fadeOutRight { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.fadeOutRight { + -webkit-animation-name: fadeOutRight; + animation-name: fadeOutRight; +} + +@-webkit-keyframes fadeOutRightBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes fadeOutRightBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.fadeOutRightBig { + -webkit-animation-name: fadeOutRightBig; + animation-name: fadeOutRightBig; +} + +@-webkit-keyframes fadeOutUp { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes fadeOutUp { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.fadeOutUp { + -webkit-animation-name: fadeOutUp; + animation-name: fadeOutUp; +} + +@-webkit-keyframes fadeOutUpBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes fadeOutUpBig { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.fadeOutUpBig { + -webkit-animation-name: fadeOutUpBig; + animation-name: fadeOutUpBig; +} + +@-webkit-keyframes flip { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +@keyframes flip { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +.animated.flip { + -webkit-backface-visibility: visible; + backface-visibility: visible; + -webkit-animation-name: flip; + animation-name: flip; +} + +@-webkit-keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInX { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInX; + animation-name: flipInX; +} + +@-webkit-keyframes flipInY { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInY { + 0% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInY; + animation-name: flipInY; +} + +@-webkit-keyframes flipOutX { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutX { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +.flipOutX { + -webkit-animation-name: flipOutX; + animation-name: flipOutX; + -webkit-animation-duration: .75s; + animation-duration: .75s; + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; +} + +@-webkit-keyframes flipOutY { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutY { + 0% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +.flipOutY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipOutY; + animation-name: flipOutY; + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes lightSpeedIn { + 0% { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes lightSpeedIn { + 0% { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.lightSpeedIn { + -webkit-animation-name: lightSpeedIn; + animation-name: lightSpeedIn; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + +@-webkit-keyframes lightSpeedOut { + 0% { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +@keyframes lightSpeedOut { + 0% { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +.lightSpeedOut { + -webkit-animation-name: lightSpeedOut; + animation-name: lightSpeedOut; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + +@-webkit-keyframes rotateIn { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateIn { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateIn { + -webkit-animation-name: rotateIn; + animation-name: rotateIn; +} + +@-webkit-keyframes rotateInDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownLeft { + -webkit-animation-name: rotateInDownLeft; + animation-name: rotateInDownLeft; +} + +@-webkit-keyframes rotateInDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownRight { + -webkit-animation-name: rotateInDownRight; + animation-name: rotateInDownRight; +} + +@-webkit-keyframes rotateInUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpLeft { + -webkit-animation-name: rotateInUpLeft; + animation-name: rotateInUpLeft; +} + +@-webkit-keyframes rotateInUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpRight { + -webkit-animation-name: rotateInUpRight; + animation-name: rotateInUpRight; +} + +@-webkit-keyframes rotateOut { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +@keyframes rotateOut { + 0% { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +.rotateOut { + -webkit-animation-name: rotateOut; + animation-name: rotateOut; +} + +@-webkit-keyframes rotateOutDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +.rotateOutDownLeft { + -webkit-animation-name: rotateOutDownLeft; + animation-name: rotateOutDownLeft; +} + +@-webkit-keyframes rotateOutDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutDownRight { + -webkit-animation-name: rotateOutDownRight; + animation-name: rotateOutDownRight; +} + +@-webkit-keyframes rotateOutUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutUpLeft { + 0% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutUpLeft { + -webkit-animation-name: rotateOutUpLeft; + animation-name: rotateOutUpLeft; +} + +@-webkit-keyframes rotateOutUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +@keyframes rotateOutUpRight { + 0% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +.rotateOutUpRight { + -webkit-animation-name: rotateOutUpRight; + animation-name: rotateOutUpRight; +} + +@-webkit-keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +@keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +.hinge { + -webkit-animation-name: hinge; + animation-name: hinge; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollIn { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes rollIn { + 0% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.rollIn { + -webkit-animation-name: rollIn; + animation-name: rollIn; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +@keyframes rollOut { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +.rollOut { + -webkit-animation-name: rollOut; + animation-name: rollOut; +} + +@-webkit-keyframes zoomIn { + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +@keyframes zoomIn { + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +.zoomIn { + -webkit-animation-name: zoomIn; + animation-name: zoomIn; +} + +@-webkit-keyframes zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInDown { + -webkit-animation-name: zoomInDown; + animation-name: zoomInDown; +} + +@-webkit-keyframes zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInLeft { + -webkit-animation-name: zoomInLeft; + animation-name: zoomInLeft; +} + +@-webkit-keyframes zoomInRight { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInRight { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInRight { + -webkit-animation-name: zoomInRight; + animation-name: zoomInRight; +} + +@-webkit-keyframes zoomInUp { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInUp { + 0% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInUp { + -webkit-animation-name: zoomInUp; + animation-name: zoomInUp; +} + +@-webkit-keyframes zoomOut { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +@keyframes zoomOut { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +.zoomOut { + -webkit-animation-name: zoomOut; + animation-name: zoomOut; +} + +@-webkit-keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutDown { + -webkit-animation-name: zoomOutDown; + animation-name: zoomOutDown; +} + +@-webkit-keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +@keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +.zoomOutLeft { + -webkit-animation-name: zoomOutLeft; + animation-name: zoomOutLeft; +} + +@-webkit-keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +@keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +.zoomOutRight { + -webkit-animation-name: zoomOutRight; + animation-name: zoomOutRight; +} + +@-webkit-keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutUp { + -webkit-animation-name: zoomOutUp; + animation-name: zoomOutUp; +} + +@-webkit-keyframes slideInDown { + 0% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes slideInDown { + 0% { + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +.slideInDown { + -webkit-animation-name: slideInDown; + animation-name: slideInDown; +} + +@-webkit-keyframes slideInLeft { + 0% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes slideInLeft { + 0% { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.slideInLeft { + -webkit-animation-name: slideInLeft; + animation-name: slideInLeft; +} + +@-webkit-keyframes slideInRight { + 0% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +@keyframes slideInRight { + 0% { + -webkit-transform: translateX(100%); + transform: translateX(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} + +.slideInRight { + -webkit-animation-name: slideInRight; + animation-name: slideInRight; +} + +@-webkit-keyframes slideInUp { + 0% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes slideInUp { + 0% { + -webkit-transform: translateY(100%); + transform: translateY(100%); + visibility: visible; + } + + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +.slideInUp { + -webkit-animation-name: slideInUp; + animation-name: slideInUp; +} + +@-webkit-keyframes slideOutDown { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(100%); + transform: translateY(100%); + } +} + +@keyframes slideOutDown { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(100%); + transform: translateY(100%); + } +} + +.slideOutDown { + -webkit-animation-name: slideOutDown; + animation-name: slideOutDown; +} + +@-webkit-keyframes slideOutLeft { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +@keyframes slideOutLeft { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(-100%); + transform: translateX(-100%); + } +} + +.slideOutLeft { + -webkit-animation-name: slideOutLeft; + animation-name: slideOutLeft; +} + +@-webkit-keyframes slideOutRight { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(100%); + transform: translateX(100%); + } +} + +@keyframes slideOutRight { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateX(100%); + transform: translateX(100%); + } +} + +.slideOutRight { + -webkit-animation-name: slideOutRight; + animation-name: slideOutRight; +} + +@-webkit-keyframes slideOutUp { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } +} + +@keyframes slideOutUp { + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + } + + 100% { + visibility: hidden; + -webkit-transform: translateY(-100%); + transform: translateY(-100%); + } +} + +.slideOutUp { + -webkit-animation-name: slideOutUp; + animation-name: slideOutUp; +} diff --git a/public/css/custom.css b/public/css/custom.css new file mode 100644 index 000000000..91ecc4499 --- /dev/null +++ b/public/css/custom.css @@ -0,0 +1,15 @@ +.box-image-text .image { + min-height: 190px; + max-height: 190px; +} + +.box-image-text .image img { + max-height: 190px; + margin: auto; +} + +.box-simple { + min-height: 230px; +} + + diff --git a/public/css/owl.carousel.css b/public/css/owl.carousel.css new file mode 100644 index 000000000..10c9276b0 --- /dev/null +++ b/public/css/owl.carousel.css @@ -0,0 +1,70 @@ +/* + * Core Owl Carousel CSS File + * v1.3.2 + */ + +/* clearfix */ +.owl-carousel .owl-wrapper:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; +} +/* display none until init */ +.owl-carousel{ + display: none; + position: relative; + width: 100%; + -ms-touch-action: pan-y; +} +.owl-carousel .owl-wrapper{ + display: none; + position: relative; + -webkit-transform: translate3d(0px, 0px, 0px); +} +.owl-carousel .owl-wrapper-outer{ + overflow: hidden; + position: relative; + width: 100%; +} +.owl-carousel .owl-wrapper-outer.autoHeight{ + -webkit-transition: height 500ms ease-in-out; + -moz-transition: height 500ms ease-in-out; + -ms-transition: height 500ms ease-in-out; + -o-transition: height 500ms ease-in-out; + transition: height 500ms ease-in-out; +} + +.owl-carousel .owl-item{ + float: left; +} +.owl-controls .owl-page, +.owl-controls .owl-buttons div{ + cursor: pointer; +} +.owl-controls { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +/* mouse grab icon */ +.grabbing { + cursor:url(/img/grabbing.png) 8 8, move; +} + +/* fix */ +.owl-carousel .owl-wrapper, +.owl-carousel .owl-item{ + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + -webkit-transform: translate3d(0,0,0); + -moz-transform: translate3d(0,0,0); + -ms-transform: translate3d(0,0,0); +} diff --git a/public/css/owl.theme.css b/public/css/owl.theme.css new file mode 100644 index 000000000..b3c430e3b --- /dev/null +++ b/public/css/owl.theme.css @@ -0,0 +1,69 @@ +/* +* Owl Carousel Owl Demo Theme +* v1.3.2 +*/ + +.owl-theme .owl-controls{ + margin-top: 10px; + text-align: center; +} + +/* Styling Next and Prev buttons */ + +.owl-theme .owl-controls .owl-buttons div{ + display: inline-block; + zoom: 1; + *display: inline;/*IE7 life-saver */ +} +/* Clickable class fix problem with hover on touch devices */ +/* Use it for non-touch hover action */ +.owl-theme .owl-controls.clickable .owl-buttons div:hover{ + filter: Alpha(Opacity=100);/*IE7 fix*/ + opacity: 1; + text-decoration: none; +} + +/* Styling Pagination*/ + +.owl-theme .owl-controls .owl-page{ + display: inline-block; + zoom: 1; + *display: inline;/*IE7 life-saver */ +} +.owl-theme .owl-controls .owl-page span{ + display: block; + width: 12px; + height: 12px; + margin: 5px 7px; + filter: Alpha(Opacity=50);/*IE7 fix*/ + opacity: 0.5; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; + background: #869791; +} + +.owl-theme .owl-controls .owl-page.active span, +.owl-theme .owl-controls.clickable .owl-page:hover span{ + filter: Alpha(Opacity=100);/*IE7 fix*/ + opacity: 1; +} + +/* If PaginationNumbers is true */ + +.owl-theme .owl-controls .owl-page span.owl-numbers{ + height: auto; + width: auto; + color: #FFF; + padding: 2px 10px; + font-size: 12px; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + border-radius: 30px; +} + +/* preloading images */ +.owl-item.loading{ + min-height: 150px; + background: url(AjaxLoader.gif) no-repeat center center +} \ No newline at end of file diff --git a/public/css/style.blue.css b/public/css/style.blue.css new file mode 100644 index 000000000..7c1660302 --- /dev/null +++ b/public/css/style.blue.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #467fbf; + --navbar-border-top: #294d76; + --button-border: #336194; + --link-focus: #2f5a89; + --form-shadow: rgba(70, 127, 191, 0.6); + --pagination-bg: #b8cee6; + --link-hover-bg: #36669c; + --navbar-focus: #92b3d9; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-bw.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-bw.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 000000000..f010ab251 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,106 @@ +.social-share { + position: relative; + top: -0.5em; +} + +.social-share ul { + margin: 0; +} + +.social-share ul li p { + display: none; +} + +.social-share .share-icons li { + padding: 0 !important; + padding-bottom: 10px !important; +} + +.social-share .share-btn { + padding: 0.25em; + width: 3em; +} + +.social-share-nav .share-btn h3{ + color: #ffffff; +} + +ul.share-icons { + cursor: default; + list-style: none; + padding-left: 0; + margin-top: 1em; +} + +ul.share-icons li { + display: inline-block; + padding: 0 1em 0 0; +} + +ul.share-icons li:last-child { + padding-right: 0; +} + +ul.share-icons li > * { + text-decoration: none; + border: 0; +} + +ul.share-icons li > *:before { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + text-transform: none !important; +} + +ul.share-icons li > * .label { + display: none; +} + +.share-btn { + display: inline-block; + color: #ffffff; + border: none; + border-radius: 4px; + box-shadow: 0 2px 0 0 rgba(0,0,0,0.2); + outline: none; + text-align: center; + text-decoration: none; +} + +.share-btn:hover { + color: #ffffff !important; +} + +.share-btn:active { + position: relative; + top: 2px; + box-shadow: none; + color: #e2e2e2; + outline: none; +} +.share-btn .widget-social__link-icon { + margin: 0; +} + +.share-btn.twitter { background: #55acee; } +.share-btn.google-plus { background: #dd4b39; } +.share-btn.facebook { background: #3B5998; } +.share-btn.linkedin { background: #4875B4; } +.share-btn.stumbleupon { background: #EB4823; } +.share-btn.pinterest { background: #BD081C; } +.share-btn.reddit { background: #ff5700; } +.share-btn.email { background: #444444; } +.share-btn.whatsapp { background: #25d366; } + + +.share-btn.twitter:hover { background: #4c9ad6; } +.share-btn.google-plus:hover { background: #c64333; } +.share-btn.facebook:hover { background: #2f4779; } +.share-btn.linkedin:hover { background: #4069a2; } +.share-btn.stumbleupon:hover { background: #d3401f; } +.share-btn.pinterest:hover { background: #AD0000; } +.share-btn.reddit:hover { background: #e54e00; } +.share-btn.email:hover { background: #363636; } \ No newline at end of file diff --git a/public/css/style.default.css b/public/css/style.default.css new file mode 100644 index 000000000..65573e4ef --- /dev/null +++ b/public/css/style.default.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #38a7bb; + --navbar-border-top: #20616d; + --button-border: #2a7d8c; + --link-focus: #267280; + --form-shadow: rgba(56, 167, 187, 0.6); + --pagination-bg: #a7dbe5; + --link-hover-bg: #2c8494; + --navbar-focus: #80cbd9; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 14px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 14px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 14px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 14px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 14px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 14px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 14px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 14px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 14px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 14px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 14px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 14px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 14px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 14px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 14px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 14px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-bw.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-bw.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 14px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 14px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 14px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 14px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 14px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 14px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 14px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 14px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.green.css b/public/css/style.green.css new file mode 100644 index 000000000..92de476a4 --- /dev/null +++ b/public/css/style.green.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #6aae7a; + --navbar-border-top: #3f734b; + --button-border: #4d8e5c; + --link-focus: #488456; + --form-shadow: rgba(106, 174, 122, 0.6); + --pagination-bg: #cde4d2; + --link-hover-bg: #519461; + --navbar-focus: #acd2b5; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-green.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-green.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.marsala.css b/public/css/style.marsala.css new file mode 100644 index 000000000..c50437f4a --- /dev/null +++ b/public/css/style.marsala.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #955251; + --navbar-border-top: #532e2d; + --button-border: #6d3c3b; + --link-focus: #633736; + --form-shadow: rgba(149, 82, 81, 0.6); + --pagination-bg: #d2adad; + --link-hover-bg: #74403f; + --navbar-focus: #c08c8c; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-bw.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-bw.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.pink.css b/public/css/style.pink.css new file mode 100644 index 000000000..3cafcc314 --- /dev/null +++ b/public/css/style.pink.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #c27baa; + --navbar-border-top: #934478; + --button-border: #af518f; + --link-focus: #a44c87; + --form-shadow: rgba(194, 123, 170, 0.6); + --pagination-bg: #f2e4ed; + --link-hover-bg: #b25894; + --navbar-focus: #e2c1d7; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-bw.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-bw.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus) +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.red.css b/public/css/style.red.css new file mode 100644 index 000000000..cb6ec295c --- /dev/null +++ b/public/css/style.red.css @@ -0,0 +1,3582 @@ +/* Themed colors */ +:root { + --primary-accent: #da4d4d; + --navbar-border-top: #a02121; + --button-border: #c22828; + --link-focus: #b52626; + --form-shadow: rgba(218, 77, 77, 0.6); + --pagination-bg: #f4cccc; + --link-hover-bg: #ca2a2a; + --navbar-focus: #eca1a1; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-bw.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-bw.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; + font-size: 18px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.turquoise.css b/public/css/style.turquoise.css new file mode 100644 index 000000000..4ad677c4e --- /dev/null +++ b/public/css/style.turquoise.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #4fbfa8; + --navbar-border-top: #2d7b6b; + --button-border: #389985; + --link-focus: #348e7b; + --form-shadow: rgba(79, 191, 168, 0.6); + --pagination-bg: #bfe8df; + --link-hover-bg: #3aa18c; + --navbar-focus: #9adacd; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-turquoise.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-turquoise.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/css/style.violet.css b/public/css/style.violet.css new file mode 100644 index 000000000..ba3dac16a --- /dev/null +++ b/public/css/style.violet.css @@ -0,0 +1,3581 @@ +/* Themed colors */ +:root { + --primary-accent: #986dbd; + --navbar-border-top: #653d87; + --button-border: #7a4aa3; + --link-focus: #724599; + --form-shadow: rgba(152, 109, 189, 0.6); + --pagination-bg: #e2d6ed; + --link-hover-bg: #7f4daa; + --navbar-focus: #cab3dd; +} + +.clearfix:before, +.clearfix:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after { + content: " "; + display: table; +} +.clearfix:after, +.navbar:after, +.navbar-header:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +/* general styles */ +a, +button { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.clickable { + cursor: pointer !important; +} +.required { + color: var(--primary-accent); +} +.accent { + color: var(--primary-accent); +} +.text-uppercase { + text-transform: uppercase; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .text-center-sm { + text-align: center; + } +} +p.lead { + margin-bottom: 40px; +} +section, +div.section { + margin-bottom: 40px; +} +.no-mb { + margin-bottom: 0 !important; +} +.mb-small { + margin-bottom: 20px !important; +} +.heading { + margin-bottom: 40px; +} +.heading h1, +.heading h2, +.heading h3, +.heading h4, +.heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; + vertical-align: middle; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.heading h1 i[class^="fa"], +.heading h2 i[class^="fa"], +.heading h3 i[class^="fa"], +.heading h4 i[class^="fa"], +.heading h5 i[class^="fa"] { + display: inline-block; + background: var(--primary-accent); + width: 30px; + height: 30px; + vertical-align: middle; + text-align: center; + color: #fff; + font-size: 12px; + line-height: 30px; + border-radius: 15px; +} +.icon { + display: inline-block; + width: 80px; + height: 80px; + color: #fff; + line-height: 80px; + border-radius: 40px; + border: solid 1px #fff; + font-size: 20px; +} +.icon.icon-lg { + font-size: 30px; + border-width: 2px; +} +.ul-icons { + padding-left: 10px; +} +.ul-icons li { + list-style-type: none; + line-height: 20px; + margin-bottom: 20px; +} +.ul-icons li i { + width: 20px; + height: 20px; + background: var(--primary-accent); + color: #fff; + text-align: center; + border-radius: 10px; + line-height: 20px; + margin-right: 10px; +} +ul.list-style-none { + list-style: none; +} +#text-page h1, +#text-page h2, +#text-page h3 { + font-weight: 700; +} +#error-page { + text-align: center; + margin-top: 40px; + margin-bottom: 100px; +} +#error-page h4 { + margin-bottom: 40px; +} +#error-page p.buttons { + margin-top: 40px; +} +.pages-listing .item { + text-align: center; +} +.pages-listing .item h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 0.08em; +} +.pages-listing .item h3 a { + color: #555555; +} +.pages-listing .item .text { + margin-bottom: 20px; +} +.pages-listing .item .text p { + color: #999999; + font-size: 12px; + margin-bottom: 20px; +} +.banner { + margin-bottom: 30px; + text-align: center; +} +.banner img { + margin: 0 auto; +} +.banner a:hover img { + opacity: 0.8; + filter: alpha(opacity=80); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.pages { + text-align: center; +} +.pages .loadMore { + text-align: center; +} +.pages .pagination { + text-align: center; +} +.features-buttons button { + margin-bottom: 20px; +} +@media (min-width: 1300px) { + body.boxed { + background: url(https://www.toptal.com/designers/subtlepatterns/patterns/subtle_zebra_3d.png); + } + body.boxed #all { + position: relative; + background: #fff; + width: 1200px; + margin: 0 auto; + overflow: hidden; + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; + } +} +#top { + background: #555555; + color: #eeeeee; + padding: 10px 0; +} +#top p { + margin: 0; + font-size: 12px; +} +#top .social { + float: right; + text-align: right; +} +#top .social a { + color: #999999; + display: inline-block; + width: 24px; + height: 24px; + border-radius: 12px; + line-height: 24px; + font-size: 12px; + text-align: center; +} +#top .social a:hover { + color: #fff; + background: var(--primary-accent); + -webkit-transform: scale(1.1); + transform: scale(1.1); +} +#top .social a:hover.facebook { + background-color: #4460ae; +} +#top .social a:hover.gplus { + background-color: #c21f25; +} +#top .social a:hover.twitter { + background-color: #3cf; +} +#top .social a:hover.instagram { + background-color: #cd4378; +} +#top .social a:hover.email { + background-color: #4a7f45; +} +#top .login { + float: right; +} +#top .login a { + font-size: 12px; + color: #eeeeee; + margin-right: 15px; + text-decoration: none; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.10em; +} +@media (max-width: 767px) { + #top .login { + float: left; + } +} +#top.light { + background: #fff; + color: #999999; + border-bottom: solid 1px #eeeeee; +} +#top.light .login a { + color: #555555; +} +.navbar { + border: none; +} +.navbar ul.nav > li > a { + text-transform: uppercase; + text-decoration: underline; + font-weight: bold; + letter-spacing: 0.08em; + border-top: solid 5px transparent; +} +.navbar ul.nav > li > a:hover { + border-top: solid 5px var(--primary-accent); +} +.navbar ul.nav > li.active > a, +.navbar ul.nav > li.open > a { + text-decoration: none !important; + border-top: solid 5px var(--navbar-border-top); +} +@media (max-width: 768px) { + .navbar ul.nav > li.active > a, + .navbar ul.nav > li.open > a { + border-top-color: transparent; + } + .navbar ul.nav > li > a:hover { + border-top-color: transparent; + } +} +.navbar.navbar-light ul.nav > li.active > a { + border-top: solid 5px var(--navbar-border-top); + background: #fff !important; + color: #555555 !important; +} +.navbar.navbar-light ul.nav > li.active > a:hover { + border-top: solid 5px var(--navbar-border-top); +} +.navbar.navbar-light ul.nav > li > a:hover, +.navbar.navbar-light ul.nav > li.open > a:hover, +.navbar.navbar-light ul.nav > li > a:focus, +.navbar.navbar-light ul.nav > li.open > a:focus { + border-top: solid 5px var(--primary-accent); + background: #fff !important; + color: #555555 !important; +} +.navbar ul.dropdown-menu { + margin: 0; + padding: 0; +} +.navbar ul.dropdown-menu li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 4px 0; +} +.navbar ul.dropdown-menu li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + left: 0; +} +.navbar ul.dropdown-menu li a:hover { + color: var(--primary-accent); + text-decoration: none; + background: none; + left: 2px; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .navbar ul.dropdown-menu li a:hover { + left: 0; + } +} +.navbar .yamm-content h3 { + font-size: 18px; + text-transform: uppercase; + padding-bottom: 10px; + margin-top: 5px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +@media (max-width: 767px) { + .navbar .yamm-content h3 { + font-size: 14px; + } +} +.navbar .yamm-content h5 { + text-transform: uppercase; + padding-bottom: 10px; + border-bottom: dotted 1px #555555; + letter-spacing: 0.08em; +} +.navbar .yamm-content ul { + margin: 0; + padding: 0; +} +.navbar .yamm-content ul li { + list-style-type: none; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + padding: 4px 0; +} +.navbar .yamm-content ul li a { + position: relative; + color: #999999; + font-size: 12px; + display: block; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.navbar .yamm-content ul li a:hover { + color: var(--primary-accent); + text-decoration: none; + padding-left: 2px; +} +.navbar .yamm-content .banner { + margin-bottom: 10px; +} +.navbar .yamm-fw .dropdown-menu { + padding: 0; +} +.navbar .navbar-buttons { + float: right; +} +.navbar .navbar-buttons button, +.navbar .navbar-buttons a.btn, +.navbar .navbar-buttons .btn-default.navbar-toggle { + margin-top: 11px; + margin-bottom: 11px; + margin-left: 0; + margin-right: 5px; +} +.navbar .btn-default, +.navbar .btn-default.navbar-toggle { + color: #999999; + background-color: #fff; + margin-left: 7px; + margin-right: 0; +} +.navbar .btn-default:hover, +.navbar .btn-default.navbar-toggle:hover, +.navbar .btn-default:focus, +.navbar .btn-default.navbar-toggle:focus { + background-color: #fff; + border-color: var(--primary-accent); + color: var(--primary-accent); +} +.navbar #search { + clear: both; + border-top: solid 1px var(--primary-accent); + text-align: right; +} +.navbar #search form { + float: right; +} +.navbar #search form .input-group { + width: 500px; +} +@media (max-width: 768px) { + .navbar #search form .input-group { + width: 100%; + } +} +.navbar #basket-overview a { + margin-left: 7px; +} +.navbar-affixed-top { + top: 0; + z-index: 1000; + width: 100%; +} +.navbar-affixed-top.affix { + -webkit-box-shadow: 0 0 5px #cccccc; + box-shadow: 0 0 5px #cccccc; +} +.navbar-affixed-top.affix + section { + margin-top: 62px; +} +@supports (position: sticky) { + .navbar-affixed-top { + position: sticky; + } + .navbar-affixed-top.affix + section { + margin-top: 0; + } +} +#login-modal { + overflow: hidden; +} +#login-modal .modal-header h4 { + text-transform: uppercase; +} +#login-modal form { + margin-bottom: 20px; +} +#login-modal a { + color: var(--primary-accent); +} +#login-modal p { + font-weight: 300; + margin-bottom: 20px; + font-size: 13px; +} +/* buttons */ +.btn { + font-weight: 700; + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 0; +} +.input-group .btn { + font-size: 14px; +} +.btn-lg { + padding: 10px 16px; + font-size: 14px; + line-height: 1.33; + border-radius: 0; +} +.btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0; +} +.btn-template-main { + color: var(--primary-accent); + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + color: var(--primary-accent); + background-color: #e6e6e6; + border-color: var(--button-border); +} +.btn-template-main:active, +.btn-template-main.active, +.open > .dropdown-toggle.btn-template-main { + background-image: none; +} +.btn-template-main.disabled, +.btn-template-main[disabled], +fieldset[disabled] .btn-template-main, +.btn-template-main.disabled:hover, +.btn-template-main[disabled]:hover, +fieldset[disabled] .btn-template-main:hover, +.btn-template-main.disabled:focus, +.btn-template-main[disabled]:focus, +fieldset[disabled] .btn-template-main:focus, +.btn-template-main.disabled:active, +.btn-template-main[disabled]:active, +fieldset[disabled] .btn-template-main:active, +.btn-template-main.disabled.active, +.btn-template-main[disabled].active, +fieldset[disabled] .btn-template-main.active { + background-color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-main .badge { + color: #ffffff; + background-color: var(--primary-accent); +} +.btn-template-main:hover, +.btn-template-main:focus, +.btn-template-main:active, +.btn-template-main.active { + background: var(--primary-accent); + color: #ffffff; + border-color: var(--primary-accent); +} +.btn-template-transparent-primary { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active, +.open > .dropdown-toggle.btn-template-transparent-primary { + background-image: none; +} +.btn-template-transparent-primary.disabled, +.btn-template-transparent-primary[disabled], +fieldset[disabled] .btn-template-transparent-primary, +.btn-template-transparent-primary.disabled:hover, +.btn-template-transparent-primary[disabled]:hover, +fieldset[disabled] .btn-template-transparent-primary:hover, +.btn-template-transparent-primary.disabled:focus, +.btn-template-transparent-primary[disabled]:focus, +fieldset[disabled] .btn-template-transparent-primary:focus, +.btn-template-transparent-primary.disabled:active, +.btn-template-transparent-primary[disabled]:active, +fieldset[disabled] .btn-template-transparent-primary:active, +.btn-template-transparent-primary.disabled.active, +.btn-template-transparent-primary[disabled].active, +fieldset[disabled] .btn-template-transparent-primary.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-primary .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-primary:hover, +.btn-template-transparent-primary:focus, +.btn-template-transparent-primary:active, +.btn-template-transparent-primary.active { + background: #fff; + color: var(--primary-accent); + border-color: #fff; +} +.btn-template-transparent-black { + color: #ffffff; + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + color: #ffffff; + background-color: rgba(0, 0, 0, 0); + border-color: #e0e0e0; +} +.btn-template-transparent-black:active, +.btn-template-transparent-black.active, +.open > .dropdown-toggle.btn-template-transparent-black { + background-image: none; +} +.btn-template-transparent-black.disabled, +.btn-template-transparent-black[disabled], +fieldset[disabled] .btn-template-transparent-black, +.btn-template-transparent-black.disabled:hover, +.btn-template-transparent-black[disabled]:hover, +fieldset[disabled] .btn-template-transparent-black:hover, +.btn-template-transparent-black.disabled:focus, +.btn-template-transparent-black[disabled]:focus, +fieldset[disabled] .btn-template-transparent-black:focus, +.btn-template-transparent-black.disabled:active, +.btn-template-transparent-black[disabled]:active, +fieldset[disabled] .btn-template-transparent-black:active, +.btn-template-transparent-black.disabled.active, +.btn-template-transparent-black[disabled].active, +fieldset[disabled] .btn-template-transparent-black.active { + background-color: transparent; + border-color: #ffffff; +} +.btn-template-transparent-black .badge { + color: transparent; + background-color: #ffffff; +} +.btn-template-transparent-black:hover, +.btn-template-transparent-black:focus, +.btn-template-transparent-black:active, +.btn-template-transparent-black.active { + background: #fff; + color: #000; + border-color: #fff; +} +.btn-template-primary { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary:hover, +.btn-template-primary:focus, +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + color: #ffffff; + background-color: var(--link-hover-bg); + border-color: var(--button-border); +} +.btn-template-primary:active, +.btn-template-primary.active, +.open > .dropdown-toggle.btn-template-primary { + background-image: none; +} +.btn-template-primary.disabled, +.btn-template-primary[disabled], +fieldset[disabled] .btn-template-primary, +.btn-template-primary.disabled:hover, +.btn-template-primary[disabled]:hover, +fieldset[disabled] .btn-template-primary:hover, +.btn-template-primary.disabled:focus, +.btn-template-primary[disabled]:focus, +fieldset[disabled] .btn-template-primary:focus, +.btn-template-primary.disabled:active, +.btn-template-primary[disabled]:active, +fieldset[disabled] .btn-template-primary:active, +.btn-template-primary.disabled.active, +.btn-template-primary[disabled].active, +fieldset[disabled] .btn-template-primary.active { + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.btn-template-primary .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +#intro { + background: url('../img/home.jpg') no-repeat center top; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} +#intro .item { + font-family: "Roboto", Helvetica, Arial, sans-serif; + height: 100%; +} +#intro .item h1 { + text-transform: uppercase; + font-size: 50px; + color: #fff; + margin-bottom: 40px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #intro .item h1 { + font-size: 40px; + } +} +@media (max-width: 767px) { + #intro .item h1 { + font-size: 25px; + } +} +#intro .item h3 { + color: #fff; + margin-bottom: 40px; +} +@media (max-width: 767px) { + #intro .item h3 { + font-size: 15px; + margin-bottom: 20px; + } +} +#intro .item .btn { + text-transform: none; +} +@media (max-width: 991px) { + #intro .item .btn { + font-size: 14px; + } +} +@media (max-width: 991px) { + #intro .item .carousel-caption { + left: 10%; + right: 10%; + } +} +#intro .container, +#intro .row { + height: 100%; + position: relative; +} +.jumbotron { + padding: 30px; + margin-bottom: 0; + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.jumbotron .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3, +.jumbotron p, +.jumbotron ul { + color: #fff; +} +.jumbotron h1, +.jumbotron h2, +.jumbotron h3 { + color: #ffffff; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.jumbotron p { + margin-bottom: 20px; + font-size: 21px; + font-weight: 400; +} +.jumbotron p.text-uppercase { + font-weight: 700; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 0; +} +.jumbotron .container { + max-width: 100%; + z-index: 2; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 46px; + } +} +#categoryMenu h3 { + padding: 20px; + background: #f7f7f7; + margin: 0; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.panel.sidebar-menu h3 { + padding: 5px 0; + margin: 0; +} +.panel.sidebar-menu { + background: #fff; + margin: 0 0 20px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.panel.sidebar-menu .panel-heading { + text-transform: uppercase; + margin-bottom: 10px; + background: none; + padding: 0; + letter-spacing: 0.08em; + border-bottom: none; +} +.panel.sidebar-menu .panel-heading h1, +.panel.sidebar-menu .panel-heading h2, +.panel.sidebar-menu .panel-heading h3, +.panel.sidebar-menu .panel-heading h4, +.panel.sidebar-menu .panel-heading h5 { + display: inline-block; + border-bottom: solid 5px var(--primary-accent); + line-height: 1.1; + margin-bottom: 0; + padding-bottom: 10px; +} +.panel.sidebar-menu .panel-heading .btn.btn-danger { + color: #fff; + margin-top: 5px; +} +.panel.sidebar-menu .panel-body { + padding: 0; +} +.panel.sidebar-menu .panel-body span.colour { + display: inline-block; + width: 15px; + height: 15px; + border: solid 1px #555555; + vertical-align: top; + margin-top: 2px; + margin-left: 5px; +} +.panel.sidebar-menu .panel-body span.colour.white { + background: #fff; +} +.panel.sidebar-menu .panel-body span.colour.red { + background: red; +} +.panel.sidebar-menu .panel-body span.colour.green { + background: green; +} +.panel.sidebar-menu .panel-body span.colour.blue { + background: blue; +} +.panel.sidebar-menu .panel-body span.colour.yellow { + background: yellow; +} +.panel.sidebar-menu .panel-body label { + color: #999999; + font-size: 12px; +} +.panel.sidebar-menu .panel-body label:hover { + color: #555555; +} +.panel.sidebar-menu ul.nav.category-menu { + margin-bottom: 20px; + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.08em; +} +.panel.sidebar-menu ul.nav.category-menu li a { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.panel.sidebar-menu ul.nav ul { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.nav ul li { + display: block; +} +.panel.sidebar-menu ul.nav ul li a { + position: relative; + font-family: "Times New Roman", Times, serif; + font-weight: normal; + text-transform: none !important; + display: block; + padding: 10px 15px; + padding-left: 30px; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.nav ul li a:hover, +.panel.sidebar-menu ul.nav ul li a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.panel.sidebar-menu ul.tag-cloud { + list-style: none; + padding-left: 0; +} +.panel.sidebar-menu ul.tag-cloud li { + display: inline-block; +} +.panel.sidebar-menu ul.tag-cloud li a { + display: inline-block; + padding: 5px; + border: solid 1px #eeeeee; + border-radius: 0; + color: var(--primary-accent); + margin: 5px 5px 5px 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-size: 12px; + text-decoration: none; +} +.panel.sidebar-menu ul.tag-cloud li a:hover { + color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a { + color: #FFFFFF; + background-color: var(--primary-accent); +} +.panel.sidebar-menu ul.tag-cloud li.active a:hover { + color: #FFFFFF; +} +.panel.sidebar-menu ul.popular, +.panel.sidebar-menu ul.recent { + list-style: none; + padding-left: 0; + padding: 20px 0; +} +.panel.sidebar-menu ul.popular li, +.panel.sidebar-menu ul.recent li { + margin-bottom: 10px; + padding: 5px 0; + border-bottom: dotted 1px #eeeeee; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li:before, +.panel.sidebar-menu ul.recent li:before, +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + content: " "; + display: table; +} +.panel.sidebar-menu ul.popular li:after, +.panel.sidebar-menu ul.recent li:after { + clear: both; +} +.panel.sidebar-menu ul.popular li img, +.panel.sidebar-menu ul.recent li img { + width: 50px; + margin-right: 10px; +} +.panel.sidebar-menu ul.popular li h5, +.panel.sidebar-menu ul.recent li h5 { + margin: 0 0 10px; +} +.panel.sidebar-menu ul.popular li h5 a, +.panel.sidebar-menu ul.recent li h5 a { + font-weight: normal; +} +.panel.sidebar-menu ul.popular li p.date, +.panel.sidebar-menu ul.recent li p.date { + float: right; + font-size: 12px; + color: #999999; +} +.panel.sidebar-menu ul.popular li:last-child, +.panel.sidebar-menu ul.recent li:last-child { + border-bottom: none; +} +.panel.sidebar-menu .text-widget { + font-size: 12px; +} +.panel.sidebar-menu.with-icons ul.nav li a:after { + font-family: 'FontAwesome'; + content: "\f105"; + position: relative; + top: 0; + float: right; +} +/* ribbons for product sales etc. */ +.ribbon { + position: absolute; + top: 50px; + padding-left: 51px; + font-weight: 700; + letter-spacing: 0.08em; +} +.ribbon .ribbon-background { + position: absolute; + top: 0; + right: 0; +} +.ribbon .theribbon { + position: relative; + width: 80px; + padding: 6px 20px 6px 20px; + margin: 30px 10px 10px -71px; + color: #fff; + background-color: var(--primary-accent); + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.ribbon .theribbon:before, +.ribbon .theribbon:after { + content: ' '; + position: absolute; + width: 0; + height: 0; +} +.ribbon .theribbon:after { + left: 0px; + top: 100%; + border-width: 5px 10px; + border-style: solid; + border-color: #000000 #000000 transparent transparent; +} +.ribbon.sale { + top: 0; +} +.ribbon.new { + top: 50px; +} +.ribbon.new .theribbon { + background-color: #5bc0de; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.new .theribbon:after { + border-color: #2390b0 #2390b0 transparent transparent; +} +.ribbon.gift { + top: 100px; +} +.ribbon.gift .theribbon { + background-color: #5cb85c; + text-shadow: 0px 1px 2px #bbb; +} +.ribbon.gift .theribbon:after { + border-color: #357935 #357935 transparent transparent; +} +.owl-carousel .owl-controls .owl-page.active span, +.owl-theme .owl-controls .owl-page.active span, +.owl-carousel .owl-controls.clickable .owl-page:hover span, +.owl-theme .owl-controls.clickable .owl-page:hover span { + background: var(--primary-accent); +} +.owl-carousel .owl-controls .owl-buttons, +.owl-theme .owl-controls .owl-buttons { + position: absolute; + top: 5px; + right: 0; +} +.owl-carousel .owl-controls .owl-buttons div, +.owl-theme .owl-controls .owl-buttons div { + width: 26px; + height: 26px; + line-height: 25px; + margin: 0 5px 0 0; + font-size: 18px; + color: var(--primary-accent); + padding: 0; + background: #fff; + border-radius: 13px; + vertical-align: middle; + text-align: center; + opacity: 1; + filter: alpha(opacity=100); +} +.home-carousel { + position: relative; + background: url('../img/photogrid.jpg') center center repeat; + background-size: cover; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.home-carousel .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--primary-accent); + opacity: 0.9; + filter: alpha(opacity=90); +} +.home-carousel .owl-carousel { + padding-top: 60px; + padding-bottom: 20px; +} +.home-carousel .owl-theme .owl-controls .owl-page span { + background: #666; +} +.home-carousel .owl-theme .owl-controls .owl-page.active span { + background: #fff; +} +.home-carousel .owl-theme .owl-controls .owl-page:hover span { + background: #fff; +} +@media (max-width: 767px) { + .home-carousel { + text-align: center !important; + } +} +@media (min-width: 992px) { + .home-carousel .right { + text-align: right; + } +} +.home-carousel h1, +.home-carousel h2, +.home-carousel h3, +.home-carousel p, +.home-carousel ul { + color: #fff; +} +.home-carousel h1 { + font-weight: 700; + text-transform: uppercase; + font-size: 46px; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + .home-carousel h1 { + font-size: 36px; + } +} +.home-carousel h2 { + font-weight: 700; + text-transform: uppercase; + font-size: 40px; + letter-spacing: 0.08em; +} +.home-carousel ul, +.home-carousel p { + font-size: 18px; + font-weight: 700; + padding: 0; + text-transform: uppercase; + letter-spacing: 0.10em; +} +@media (max-width: 991px) { + .home-carousel ul, + .home-carousel p { + font-size: 14px; + } +} +.home-carousel ul li { + margin-bottom: 10px; +} +.customers { + padding: 0; + margin-bottom: 40px; +} +.customers .item { + list-style-type: none; + text-align: center; + margin: 0 20px; +} +.customers .item img { + display: inline-block; + filter: url("data:image/svg+xml;utf8,#grayscale"); + /* Firefox 10+, Firefox on Android */ + filter: gray; + /* IE6-9 */ + -webkit-filter: grayscale(100%); + /* Chrome 19+, Safari 6+, Safari 6+ iOS */ + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.customers .item img:hover { + max-width: auto; + filter: none; + -webkit-filter: none; +} +.testimonials { + padding: 0; + margin-bottom: 40px; +} +.testimonials .item { + list-style-type: none; + margin: 0 5px; + background: #fff; + padding-bottom: 60px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.testimonials .item .testimonial { + position: relative; + padding: 20px; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial:before, +.testimonials .item .testimonial:after { + content: " "; + display: table; +} +.testimonials .item .testimonial:after { + clear: both; +} +.testimonials .item .testimonial .text { + color: #999999; + margin-bottom: 40px; +} +.testimonials .item .testimonial .bottom { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px; + height: 50px; +} +.testimonials .item .testimonial .bottom .icon { + color: var(--primary-accent); + font-size: 30px; + float: left; + width: 20%; +} +.testimonials .item .testimonial .name-picture { + float: right; + width: 80%; + text-align: right; +} +.testimonials .item .testimonial .name-picture h5 { + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.testimonials .item .testimonial .name-picture p { + color: #999999; + margin: 0; + font-size: 12px; +} +.testimonials .item .testimonial .name-picture img { + float: right; + width: 60px; + border-radius: 30px; + margin-left: 10px; +} +.team-member { + text-align: center; + margin-bottom: 40px; +} +.team-member h3 { + font-size: 18px; + text-transform: uppercase; + margin-bottom: 5px; + letter-spacing: 0.08em; +} +.team-member h3 a { + color: #555555; +} +.team-member p.role { + color: #999999; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.team-member .social { + margin-bottom: 20px; +} +.team-member .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email { + background-color: #4a7f45; +} +.team-member .text p { + color: #999999; + font-size: 12px; +} +.team-member .social, +.team-member-detail .social { + margin-bottom: 20px; +} +.team-member .social a, +.team-member-detail .social a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +.team-member .social a i, +.team-member-detail .social a i { + vertical-align: bottom; + line-height: 26px; +} +.team-member .social a.facebook, +.team-member-detail .social a.facebook { + background-color: #4460ae; +} +.team-member .social a.gplus, +.team-member-detail .social a.gplus { + background-color: #c21f25; +} +.team-member .social a.twitter, +.team-member-detail .social a.twitter { + background-color: #3cf; +} +.team-member .social a.instagram, +.team-member-detail .social a.instagram { + background-color: #cd4378; +} +.team-member .social a.email, +.team-member-detail .social a.email { + background-color: #4a7f45; +} +.box-simple { + text-align: center; + margin-bottom: 40px; +} +.box-simple .icon { + color: var(--primary-accent); + border-color: var(--primary-accent); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +.box-simple h3 { + font-weight: normal; + font-size: 18px; + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-simple h3 a { + color: #555555; +} +.box-simple p { + color: #999999; +} +.box-simple:hover .icon { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.box-simple:hover .icon i { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.box-simple.box-white { + padding: 20px; + border: dotted 1px #999999; +} +.box-simple.box-white .icon { + color: #555555; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark { + padding: 20px; + border: dotted 1px #999999; + background: #555555; + color: #fff; +} +.box-simple.box-dark .icon { + color: #f7f7f7; + border-color: transparent; + font-size: 70px; +} +.box-simple.box-dark h3 { + color: #fff; +} +.box-simple.box-dark h3 a { + color: #fff; +} +.box-simple.box-dark p { + color: #fff; +} +.box-image { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +.box-image-text { + position: relative; + overflow: hidden; + text-align: center; + margin: 15px 0; +} +.box-image-text .top { + position: relative; + margin-bottom: 10px; +} +.box-image-text .top .bg { + position: absolute; + top: auto; + bottom: 0; + width: 100%; + height: 100%; + opacity: 0; + filter: alpha(opacity=0); + background: var(--primary-accent); +} +.box-image-text .top .name { + position: absolute; + width: 100%; + height: 50%; + bottom: 0; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .top .name h3 { + color: #fff; + text-transform: uppercase; + font-size: 18px; + letter-spacing: 0.08em; +} +.box-image-text .top .name h3 a { + color: #fff; + text-decoration: none; +} +.box-image-text .top .text { + position: absolute; + width: 100%; + height: 50%; + top: 0; + -webkit-transform: translate(0, -150%); + -ms-transform: translate(0, -150%); + -o-transform: translate(0, -150%); + transform: translate(0, -150%); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + color: #fff; + padding: 0 20px; +} +.box-image-text .content h3, +.box-image-text .content h4 { + text-transform: uppercase; + line-height: 1.5; + color: #555555; + font-weight: 800; + letter-spacing: 0.08em; +} +.box-image-text .content p { + color: #999999; +} +.box-image-text:hover .bg { + opacity: 0.7; + filter: alpha(opacity=70); +} +.box-image-text:hover .name { + position: absolute; + -webkit-transform: translate(0, -75%); + -ms-transform: translate(0, -75%); + -o-transform: translate(0, -75%); + transform: translate(0, -75%); +} +.box-image-text:hover .text { + position: absolute; + -webkit-transform: translate(0, 100%); + -ms-transform: translate(0, 100%); + -o-transform: translate(0, 100%); + transform: translate(0, 100%); +} +/* universal box */ +.box { + background: #fff; + margin: 0 0 30px; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 20px 0; + border-left: none; + border-right: none; +} +.box .box-header { + background: #f7f7f7; + margin: -20px 0 20px; + padding: 20px; + border-bottom: solid 1px #eeeeee; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-header:before, +.box .box-header:after { + content: " "; + display: table; +} +.box .box-header:after { + clear: both; +} +.box .box-footer { + background: #f7f7f7; + margin: 30px 0 -20px; + padding: 20px; + border-top: solid 1px #eeeeee; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +.box .box-footer:before, +.box .box-footer:after { + content: " "; + display: table; +} +.box .box-footer:after { + clear: both; +} +@media (max-width: 991px) { + .box .box-footer .btn { + margin-bottom: 20px; + } +} +.box.no-border { + border: none; +} +#heading-breadcrumbs { + background: url('../img/texture-violet.png') center center repeat; + padding: 20px 0; + margin-bottom: 40px; +} +#heading-breadcrumbs.no-mb { + margin-bottom: 0; +} +#heading-breadcrumbs h1 { + color: #333333; + text-transform: uppercase; + font-size: 30px; + font-weight: 700; + letter-spacing: 0.08em; +} +@media (max-width: 991px) { + #heading-breadcrumbs h1 { + text-align: center; + } +} +#heading-breadcrumbs ul.breadcrumb { + margin-top: 5px; + margin-bottom: 0; +} +.bar { + position: relative; + background: var(--primary-accent); + padding: 60px 0; +} +.bar.background-pentagon { + background: url('../img/texture-violet.png') center center repeat; + border-top: solid 1px #999999; + border-bottom: solid 1px #999999; +} +.bar.background-gray { + background: #eeeeee; +} +.bar.background-gray-dark { + background: #555555; +} +.bar.background-white { + background: #fff; +} +.bar.background-image-fixed-1 { + background: url('../img/fixed-background-1.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.background-image-fixed-2 { + background: url('../img/fixed-background-2.jpg') center top no-repeat; + background-attachment: fixed; + background-size: cover; +} +.bar.color-white h1, +.bar.color-white h2, +.bar.color-white h3, +.bar.color-white h4, +.bar.color-white h5, +.bar.color-white h6, +.bar.color-white p { + color: #fff; +} +.bar.padding-big { + padding: 50px 0; +} +.bar.padding-horizontal { + padding-left: 30px; + padding-right: 30px; +} +.bar.margin-vertical { + margin-top: 20px; + margin-bottom: 20px; +} +.bar .dark-mask { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #000; + opacity: 0.3; + filter: alpha(opacity=30); +} +.portfolio.no-space { + padding: 0 15px; +} +.portfolio.no-space .box-image { + margin: 0 -15px; +} +.portfolio-project .project-more h4 { + color: #555555; + text-transform: uppercase; + margin-bottom: 0; + text-align: left; + font-size: 14px; + letter-spacing: 0.08em; +} +.portfolio-project .project-more p { + color: #999999; + padding: 10px 0; + margin-bottom: 20px; + text-align: left; +} +.portfolio-showcase { + margin: 15px 0 60px; +} +.portfolio-showcase h3 a { + text-transform: uppercase; + line-height: 1.5; + letter-spacing: 0.08em; +} +.portfolio-showcase p.lead { + color: #555555; + margin-bottom: 20px; +} +.portfolio-showcase p { + color: #999999; +} +.portfolio-showcase p.buttons { + margin-top: 40px; +} +.see-more { + text-align: center; + margin-top: 20px; + padding-top: 20px; +} +.see-more p { + font-size: 28px; + font-weight: 100; + margin-bottom: 20px; +} +.showcase .item { + text-align: center; +} +.showcase .item .icon { + display: inline-block; + width: 50px; + height: 50px; + color: #555555; + line-height: 50px; + border-radius: 25px; + border: solid 1px #555555; +} +.showcase .item h4 { + color: #555555; + text-transform: uppercase; + letter-spacing: 0.08em; + line-height: 1.5; + font-size: 16px; +} +.showcase .item h4 span { + font-weight: bold; + font-size: 51px; +} +.packages .package { + background: #fff; + margin-top: 25px; + margin-bottom: 20px; + padding-bottom: 15px; + text-align: center; + border: solid 1px var(--primary-accent); + overflow: hidden; +} +.packages .package .package-header { + height: 57px; + color: #fff; + line-height: 57px; + background: var(--primary-accent); +} +.packages .package .package-header h5 { + color: #fff; + text-transform: uppercase; + font-weight: bold; + line-height: 57px; + margin: 0; + letter-spacing: 0.08em; +} +.packages .package .package-header.light-gray { + background: #eeeeee; +} +.packages .package .package-header.light-gray h5 { + color: #555555; +} +.packages .package .price { + line-height: 120px; + height: 100px; + color: #fff; + font-weight: 400; +} +.packages .package .price h4 { + display: inline; + font-size: 50px; + line-height: normal; + margin-bottom: 0; +} +.packages .package .price .period { + line-height: normal; + color: #999999; +} +.packages .package ul { + padding: 0; +} +.packages .package ul li { + list-style-type: none; + padding-top: 10px; + padding-bottom: 10px; + width: 80%; + margin: auto; + border-bottom: 1px dotted #ccc; +} +.packages .package ul li:last-child { + border-bottom: 0; +} +.packages .package ul li i { + font-size: 13px; + margin-right: 5px; +} +.packages .best-value .package { + margin-top: 0; + padding-bottom: 40px; +} +.packages .best-value .package .package-header { + height: 72px; + padding-top: 17px; + height: 82px !important; +} +.packages .best-value .package .package-header h5 { + font-weight: bold; + line-height: 29px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.packages .best-value .package .package-header .meta-text { + font-size: 13px; + line-height: 15px; +} +#map { + height: 300px; +} +#map.with-border { + border-top: solid 1px var(--primary-accent); + border-bottom: solid 1px var(--primary-accent); +} +#blog-listing-big .post, +#blog-homepage .post { + margin-bottom: 60px; +} +#blog-listing-big .post h2, +#blog-homepage .post h2, +#blog-listing-big .post h4, +#blog-homepage .post h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#blog-listing-big .post h2 a, +#blog-homepage .post h2 a, +#blog-listing-big .post h4 a, +#blog-homepage .post h4 a { + color: #555555; +} +#blog-listing-big .post h2 a:hover, +#blog-homepage .post h2 a:hover, +#blog-listing-big .post h4 a:hover, +#blog-homepage .post h4 a:hover { + color: var(--primary-accent); +} +#blog-listing-big .post .author-category, +#blog-homepage .post .author-category { + color: #999999; + text-transform: uppercase; + font-weight: 300; + letter-spacing: 0.08em; +} +#blog-listing-big .post .author-category a, +#blog-homepage .post .author-category a { + font-weight: 500; +} +#blog-listing-big .post .date-comments a, +#blog-homepage .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-big .post .date-comments a:hover, +#blog-homepage .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-big .post .date-comments, + #blog-homepage .post .date-comments { + text-align: right; + } +} +#blog-listing-big .post .intro, +#blog-homepage .post .intro { + text-align: left; +} +#blog-listing-big .post .image, +#blog-homepage .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-big .post .image img, +#blog-homepage .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-big .post .image img.img-responsive, + #blog-homepage .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-big .post .video, +#blog-homepage .post .video { + margin-bottom: 10px; +} +#blog-listing-big .post .read-more, +#blog-homepage .post .read-more { + text-align: right; +} +#blog-listing-medium .post { + margin-bottom: 60px; +} +#blog-listing-medium .post h2 { + text-transform: uppercase; + margin: 0 0 10px; + font-size: 24px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post h2 a { + color: #555555; +} +#blog-listing-medium .post h2 a:hover { + color: var(--primary-accent); +} +#blog-listing-medium .post .author-category { + float: left; + color: #999999; + text-transform: uppercase; + font-weight: 300; + font-size: 12px; + letter-spacing: 0.08em; +} +#blog-listing-medium .post .author-category a { + font-weight: 500; +} +#blog-listing-medium .post .date-comments { + float: right; + font-size: 12px; +} +#blog-listing-medium .post .date-comments a { + color: #999999; + margin-right: 20px; +} +#blog-listing-medium .post .date-comments a:hover { + color: var(--primary-accent); +} +@media (min-width: 768px) { + #blog-listing-medium .post .date-comments { + text-align: right; + } +} +#blog-listing-medium .post .intro { + text-align: left; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .clearfix:before, +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:before, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:before, +#blog-listing-medium .post .navbar-header:after { + content: " "; + display: table; +} +#blog-listing-medium .post .clearfix:after, +#blog-listing-medium .post .navbar:after, +#blog-listing-medium .post .navbar-header:after { + clear: both; +} +#blog-listing-medium .post .image { + margin-bottom: 10px; + overflow: hidden; +} +#blog-listing-medium .post .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + #blog-listing-medium .post .image img.img-responsive { + min-width: 100%; + } +} +#blog-listing-medium .post .video { + margin-bottom: 10px; +} +#blog-listing-medium .post .read-more { + text-align: right; +} +.box-image-text.blog .author-category { + color: #999999; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 300; + font-size: 12px; +} +.box-image-text.blog .author-category a { + font-weight: 500; +} +.box-image-text.blog .intro { + text-align: left; + margin-bottom: 20px; +} +#blog-homepage .post { + margin-bottom: 30px; +} +#blog-homepage .post h2, +#blog-homepage .post h4, +#blog-homepage .post .author-category, +#blog-homepage .post .read-more { + text-align: center; +} +#blog-homepage .post .read-more { + margin-top: 20px; +} +#blog-post #post-content { + margin-bottom: 20px; +} +#blog-post #post-content img{ + max-width: 100%; + height: auto; + display: block; + margin: auto; +} +#blog-post .comment { + margin-bottom: 25px; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment:before, +#blog-post .comment:after { + content: " "; + display: table; +} +#blog-post .comment:after { + clear: both; +} +#blog-post .comment .posted { + color: #999999; + font-size: 12px; +} +#blog-post .comment .reply { + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#blog-post .comment.last { + margin-bottom: 0; +} +#blog-post #comments, +#blog-post #comment-form { + padding: 20px 0; + margin-top: 20px; + border-top: solid 1px #eeeeee; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments:before, +#blog-post #comment-form:before, +#blog-post #comments:after, +#blog-post #comment-form:after { + content: " "; + display: table; +} +#blog-post #comments:after, +#blog-post #comment-form:after { + clear: both; +} +#blog-post #comments h4, +#blog-post #comment-form h4 { + margin-bottom: 20px; +} +#blog-post #comment-form { + margin-bottom: 20px; +} +.product { + background: #fff; + border-bottom: solid 1px #e6e6e6; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 60px; + overflow: hidden; + text-align: center; +} +.product .image { + overflow: hidden; +} +.product .image img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +@media (max-width: 767px) { + .product .image img.img-responsive { + min-width: 100%; + } +} +.product .text { + padding: 10px; +} +.product .text h3 { + font-size: 14px; + font-weight: 700; + height: 39.6px; + text-transform: uppercase; + letter-spacing: 0.08em; +} +.product .text h3 a { + color: #555555; +} +.product .text h3 a:hover { + text-decoration: none; +} +.product .text p.price { + font-size: 18px; +} +.product .text p.price del { + color: #999999; +} +.product .buttons { + clear: both; + position: absolute; + display: none; + bottom: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: solid 1px transparent; + padding: 20px; + background: rgba(255, 255, 255, 0.9); + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + text-align: center; +} +.product .buttons .btn { + margin-bottom: 20px; +} +.product:hover { + border-bottom: solid 1px #808080; + top: 0; +} +.product:hover .buttons { + clear: both; + position: absolute; + top: 0; + background: rgba(255, 255, 255, 0.5); +} +.product:hover .image img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +.goToDescription { + font-size: 12px; + text-align: center; + margin-bottom: 40px; +} +.goToDescription a { + color: #999999; + text-decoration: underline; +} +#productMain { + margin-bottom: 30px; +} +#productMain .sizes { + text-align: center; +} +#productMain .sizes h3 { + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 40px; +} +#productMain .sizes a { + display: inline-block; + width: 40px; + height: 40px; + border-radius: 40px; + background: #ccc; + line-height: 40px; + color: #555555; + text-align: center; + text-decoration: none; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +#productMain .sizes a.active, +#productMain .sizes a:hover { + background: var(--primary-accent); + color: #fff; +} +#productMain .sizes input { + display: none; +} +#productMain .price { + font-size: 40px; + text-align: center; + margin-top: 40px; + margin-bottom: 40px; +} +#thumbs a { + display: block; + border: solid 1px transparent; +} +#thumbs a.active { + border-color: var(--primary-accent); +} +#product-social { + text-align: center; +} +#product-social h4 { + font-weight: 300; + margin-bottom: 10px; +} +#product-social p { + line-height: 26px; +} +#product-social p a { + margin: 0 10px 0 0; + color: #fff; + display: inline-block; + width: 26px; + height: 26px; + border-radius: 13px; + line-height: 26px; + font-size: 15px; + text-align: center; + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; + vertical-align: bottom; +} +#product-social p a i { + vertical-align: bottom; + line-height: 26px; +} +#product-social p a.facebook { + background-color: #4460ae; +} +#product-social p a.gplus { + background-color: #c21f25; +} +#product-social p a.twitter { + background-color: #3cf; +} +#product-social p a.instagram { + background-color: #cd4378; +} +#product-social p a.email { + background-color: #4a7f45; +} +@media (max-width: 991px) { + #product-social { + text-align: center; + } +} +#checkout .nav { + margin-bottom: 20px; + border-bottom: solid 1px var(--primary-accent); +} +#checkout .nav li { + height: 100%; +} +#checkout .nav li a { + display: block; + height: 100%; +} +#order-summary table { + margin-top: 20px; +} +#order-summary table td { + color: #999999; +} +#order-summary table tr.total td, +#order-summary table tr.total th { + font-size: 18px; + color: #555555; + font-weight: 700; +} +#checkout .table tbody tr td, +#basket .table tbody tr td, +#customer-order .table tbody tr td { + vertical-align: middle; +} +#checkout .table tbody tr td input, +#basket .table tbody tr td input, +#customer-order .table tbody tr td input { + width: 50px; + text-align: right; +} +#checkout .table tbody tr td img, +#basket .table tbody tr td img, +#customer-order .table tbody tr td img { + width: 50px; +} +#checkout .table tfoot, +#basket .table tfoot, +#customer-order .table tfoot { + font-size: 18px; +} +.shipping-method h4, +.payment-method h4 { + text-transform: uppercase; + letter-spacing: 0.08em; +} +#customer-orders table tr th, +#customer-orders table tr td { + vertical-align: baseline; +} +#customer-order .table tfoot th { + font-size: 18px; + font-weight: 300; +} +#customer-order .addresses { + text-align: right; + margin-bottom: 30px; +} +#customer-order .addresses p { + font-size: 18px; + font-weight: 300; +} +#customer-account { + margin-bottom: 30px; +} +#get-it { + background: var(--primary-accent); + padding: 50px 0 30px; + color: #fff; + text-align: center; +} +#get-it h1, +#get-it h2, +#get-it h3, +#get-it h4, +#get-it h5, +#get-it h6 { + color: #fff; + text-transform: uppercase; + letter-spacing: 0.08em; + margin: 0 0 20px; +} +#get-it p { + margin: 0 0 20px; +} +#footer { + background: #555555; + padding: 50px 0; + color: #999999; +} +#footer h1, +#footer h2, +#footer h3, +#footer h4, +#footer h5, +#footer h6 { + color: #eeeeee; +} +#footer h4 { + font-size: 14px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.08em; +} +#footer ul { + padding-left: 0; + list-style: none; +} +#footer ul a { + color: #999999; +} +#footer ul a:hover { + color: var(--primary-accent); + text-decoration: none; +} +#footer .photostream div { + float: left; + display: block; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 33%; + padding: 7.5px; + overflow: hidden; +} +#footer .photostream div a { + border: solid 1 px #eeeeee; +} +#footer .photostream div img { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} +#footer .photostream div:hover img { + -webkit-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -o-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); +} +#footer .blog-entries .item { + clear: both; + padding: 5px 0; + margin-bottom: 10px; + border-bottom: solid 1px #555555; +} +#footer .blog-entries .item .image { + float: left; + width: 15%; + margin-right: 10px; +} +#footer .blog-entries .item .name { + width: 75%; + margin-left: 10px; + display: table-cell; + vertical-align: middle; +} +#footer .blog-entries .item .name h5 { + margin: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 12px; +} +#footer .blog-entries .item .name h5 a { + color: #eeeeee; +} +#footer .blog-entries .item .text { + width: 100%; + clear: both; +} +#footer .blog-entries .item:last-child { + border-bottom: none; + margin-bottom: 0; +} +#footer .social a { + color: #555555; + font-size: 25px; + margin: 0 10px 0 0; +} +#footer .social a:hover { + color: var(--primary-accent); +} +#copyright { + background: #333; + color: #ccc; + padding: 50px 0; + font-size: 12px; + line-height: 28px; +} +#copyright p { + margin: 0; +} +@media (max-width: 991px) { + #copyright p { + float: none !important; + text-align: center; + margin-bottom: 10px; + } +} +[data-animate] { + opacity: 0; + filter: alpha(opacity=0); +} +#style-switch-button { + position: fixed; + top: 100px; + left: 0px; + border-radius: 0; +} +#style-switch { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 300px; + padding: 20px; + position: fixed; + top: 140px; + left: 0; + background: #fff; + border: solid 1px #eeeeee; +} +@media (max-width: 991px) { + #style-switch-button { + display: none; + } + #style-switch { + display: none; + } +} +/* Original Boostrap template overwrite */ +/* breadcrumbs */ +.breadcrumb { + font-family: "Roboto", Helvetica, Arial, sans-serif; + text-transform: uppercase; + background-color: none; + letter-spacing: 0.08em; +} +/* nav */ +.nav > li > a { + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + background-color: #eeeeee; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eeeeee; + border-color: var(--primary-accent); +} +.nav-tabs { + border-bottom: 1px solid var(--primary-accent); +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 0 0 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee var(--primary-accent); +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; + border-bottom: solid 1px var(--primary-accent); +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + /*margin-bottom: 5px;*/ +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid var(--primary-accent); +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid var(--primary-accent); + border-radius: 0 0 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #ffffff; + } +} +.tab-content { + padding: 15px; + border: solid 1px #ddd; + border-top: none; +} +/* navbar */ +.navbar { + position: relative; + min-height: 62px; + margin-bottom: 0; + border-bottom: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-affixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-affixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; + height: 62px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand img { + max-height: 42px; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 14px; + margin-bottom: 14px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-nav { + margin: 10.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 21px; + padding-bottom: 21px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 14px; + margin-bottom: 14px; +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-btn.btn-sm { + margin-top: 16px; + margin-bottom: 16px; +} +.navbar-btn.btn-xs { + margin-top: 20px; + margin-bottom: 20px; +} +.navbar-text { + margin-top: 21px; + margin-bottom: 21px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #ffffff; + border-color: #cccccc; + border-bottom: none; +} +.navbar-default .navbar-brand { + color: #555555; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #3b3b3b; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777777; +} +.navbar-default .navbar-nav > li > a { + color: #555555; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #555555; + background-color: var(--navbar-focus); +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: var(--primary-accent); +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #cccccc; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: var(--primary-accent); + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #555555; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: var(--primary-accent); + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #555555; +} +.navbar-default .navbar-link:hover { + color: #555555; +} +.navbar-default .btn-link { + color: #555555; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #555555; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #cccccc; +} +/* scaffolding */ +body { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #555555; +} +a { + color: var(--primary-accent); + text-decoration: none; +} +a:hover, +a:focus { + color: var(--link-focus); + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.img-rounded { + border-radius: 0; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +/* breadcrumbs */ +.breadcrumb { + padding: 20px 0; + margin-bottom: 20px; + background-color: transparent; + border-radius: 0; + text-align: right; +} +.breadcrumb > li + li:before { + content: ">\00a0"; + color: #555555; +} +.breadcrumb > .active { + color: #999999; +} +@media (max-width: 991px) { + .breadcrumb { + padding: 20px 0; + text-align: center; + } +} +/* dropdowns */ +.dropdown-menu { + z-index: 1000; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + padding: 5px 20px; + line-height: 1.42857143; + color: #333333; + white-space: nowrap; +} +/* labels */ +.label { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: normal; + text-transform: uppercase; + letter-spacing: 0.08em; +} +/* forms.less */ +label { + font-weight: normal; +} +.form-control { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; +} +.form-control:focus { + border-color: var(--primary-accent); + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px var(--form-shadow); +} +.form-group { + margin-bottom: 20px; +} +/* pager*/ +.pager { + margin: 20px 0; + border-top: solid 1px #eeeeee; + padding-top: 20px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Roboto", Helvetica, Arial, sans-serif; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + background-color: #ffffff; + border: 1px solid var(--primary-accent); + border-radius: 0; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + color: #fff; + background-color: var(--primary-accent); +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + border-color: #ddd; +} +/* pagination */ +.pagination { + margin: 20px 0; + font-family: "Roboto", Helvetica, Arial, sans-serif; + border-radius: 0; +} +.pagination > li > a, +.pagination > li > span { + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: var(--primary-accent); + background-color: #ffffff; + border: 1px solid #dddddd; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: var(--primary-accent); + background-color: var(--pagination-bg); + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; +} +/* responsive utilities */ +@media (max-width: 767px) { + .text-center-xs { + text-align: center !important; + } + .text-center-xs img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .text-center-sm { + text-align: center !important; + } + .text-center-sm img { + display: block; + margin-left: auto; + margin-right: auto; + } +} +/* type */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: "Roboto", Helvetica, Arial, sans-serif; + font-weight: 900; + line-height: 1.1; + color: #333333; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 20px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 18px; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +.text-small { + font-size: 12px; +} +.text-large { + font-size: 18px; +} +.text-italic { + font-style: italic; +} +.text-primary { + color: var(--primary-accent); +} +a.text-primary:hover { + color: var(--link-hover-bg); +} +.bg-primary { + color: #fff; + background-color: var(--primary-accent); +} +a.bg-primary:hover { + background-color: var(--link-hover-bg); +} +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #999999; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 14px; + border-left: 5px solid var(--primary-accent); +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + border-right: 5px solid var(--primary-accent); +} +address { + margin-bottom: 20px; + line-height: 1.42857143; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0; + -webkit-box-shadow: 0 0 0; + box-shadow: 0 0 0; +} +.panel-heading { + border-top-right-radius: 0; + border-top-left-radius: 0; + text-transform: uppercase; + letter-spacing: 0.08em; + padding: 15px 15px; +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group.accordion .panel { + border-color: #ccc; +} +.panel-primary { + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: var(--primary-accent); + border-color: var(--primary-accent); +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: var(--primary-accent); +} +.panel-primary > .panel-heading .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: var(--primary-accent); +} +.panel-primary .panel-title { + font-weight: 300; +} +.panel-primary .panel-title a:hover { + color: #fff; + text-decoration: none; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: var(--primary-accent); + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.progress-bar-primary { + background-color: var(--primary-accent); +} +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/*! + * Yamm!3 - Yet another megamenu for Bootstrap 3 + * http://geedmo.github.com/yamm3 + * + * @geedmo - Licensed under the MIT license + */ +.yamm .nav, +.yamm .collapse, +.yamm .dropup.use-yamm, +.yamm .dropdown.use-yamm { + position: static; +} +.yamm .container { + position: relative; +} +.yamm .dropdown-menu { + left: auto; +} +.yamm .nav.navbar-right .dropdown-menu { + left: auto; + right: 0; +} +.yamm .yamm-content { + padding: 20px 30px; +} +.yamm .dropdown.yamm-fw .dropdown-menu { + left: 15px; + right: 15px; +} diff --git a/public/databricks-customer-success-stories/index.html b/public/databricks-customer-success-stories/index.html new file mode 100644 index 000000000..74e700927 --- /dev/null +++ b/public/databricks-customer-success-stories/index.html @@ -0,0 +1,435 @@ + + + + + + + + + +Databricks Customer Stories + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+

Databricks Customer Stories

+
+
+
+
+ + +
+ + +
+
+ +
+ +
+

I have worked with multiple customers on their data journey. Here are some of the public stories that I have co-authored with them.

+
+
+

Audantic

+

To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. We were able to achieve a 73% reduction in the time it took our pipeline to run as well as saving 21% on the cost of the run.

+

Ref: https://www.databricks.com/blog/2022/05/05/how-audantic-uses-databricks-delta-live-tables-to-increase-productivity-for-real-estate-market-segments.html

+
+
+

Arc Resources

+

ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. +In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

+

Ref: https://www.databricks.com/blog/2022/05/24/arc-uses-a-lakehouse-architecture-for-real-time-data-insights-that-optimize-drilling-performance-and-lower-carbon-emissions.html

+ +
+ +
+ +
+ + +
+ + + +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/faq/index.html b/public/faq/index.html new file mode 100644 index 000000000..f24955efa --- /dev/null +++ b/public/faq/index.html @@ -0,0 +1,453 @@ + + + + + + + + + +FAQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+
+

FAQ

+
+
+
+
+ + +
+ + +
+
+ +
+ +
+

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

+

1. WHAT TO DO IF I HAVE STILL NOT RECEIVED THE ORDER?

+

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

+
    +
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • +
  • Aliquam tincidunt mauris eu risus.
  • +
  • Vestibulum auctor dapibus neque.
  • +
+

2. WHAT ARE THE POSTAL RATES?

+

Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven’t heard of them accusamus labore sustainable VHS.

+

3. DO YOU SEND OVERSEAS?

+

Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven’t heard of them accusamus labore sustainable VHS.

+

4. WHY ARE YOU MORE EXPENSIVE THAN OTHERS?

+

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

+
    +
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • +
  • Aliquam tincidunt mauris eu risus.
  • +
  • Vestibulum auctor dapibus neque.
  • +
+

5. ANOTHER IMPORTANT QUESTION

+

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

+
    +
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • +
  • Aliquam tincidunt mauris eu risus.
  • +
  • Vestibulum auctor dapibus neque.
  • +
+
+
+

In case you haven’t found the answer for your question please feel free to contact us, our customer support will be happy to help you.

+
+ +
+ +
+ +
+ + +
+ + + +
+ + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Toronto, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/about/about-content-svg.svg b/public/images/about/about-content-svg.svg new file mode 100755 index 000000000..ac66d61ed --- /dev/null +++ b/public/images/about/about-content-svg.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/about/about-img.jpg b/public/images/about/about-img.jpg new file mode 100755 index 000000000..e99dcff68 Binary files /dev/null and b/public/images/about/about-img.jpg differ diff --git a/public/images/about/about-mask-svg.svg b/public/images/about/about-mask-svg.svg new file mode 100755 index 000000000..d0a7bb802 --- /dev/null +++ b/public/images/about/about-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/blog/blog-shape.svg b/public/images/blog/blog-shape.svg new file mode 100755 index 000000000..555b54535 --- /dev/null +++ b/public/images/blog/blog-shape.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/contact/abstract.png b/public/images/contact/abstract.png new file mode 100755 index 000000000..f8f1bb481 Binary files /dev/null and b/public/images/contact/abstract.png differ diff --git a/public/images/contact/github.svg b/public/images/contact/github.svg new file mode 100755 index 000000000..0862c4730 --- /dev/null +++ b/public/images/contact/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/contact/leetcode.png b/public/images/contact/leetcode.png new file mode 100755 index 000000000..8c25d29f7 Binary files /dev/null and b/public/images/contact/leetcode.png differ diff --git a/public/images/contact/linkedin.svg b/public/images/contact/linkedin.svg new file mode 100755 index 000000000..4e8aec29d --- /dev/null +++ b/public/images/contact/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/contact/location.svg b/public/images/contact/location.svg new file mode 100755 index 000000000..977777c1a --- /dev/null +++ b/public/images/contact/location.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/contact/profile-img-small.jpeg b/public/images/contact/profile-img-small.jpeg new file mode 100755 index 000000000..b070283df Binary files /dev/null and b/public/images/contact/profile-img-small.jpeg differ diff --git a/public/images/contact/resume.png b/public/images/contact/resume.png new file mode 100755 index 000000000..3599ec416 Binary files /dev/null and b/public/images/contact/resume.png differ diff --git a/public/images/favicon.png b/public/images/favicon.png new file mode 100755 index 000000000..c54eaf2ac Binary files /dev/null and b/public/images/favicon.png differ diff --git a/public/images/hero/bkp.jpg b/public/images/hero/bkp.jpg new file mode 100755 index 000000000..28636fba4 Binary files /dev/null and b/public/images/hero/bkp.jpg differ diff --git a/public/images/hero/chicago.jpg b/public/images/hero/chicago.jpg new file mode 100755 index 000000000..26143ff84 Binary files /dev/null and b/public/images/hero/chicago.jpg differ diff --git a/public/images/hero/figure-svg copy.svg b/public/images/hero/figure-svg copy.svg new file mode 100755 index 000000000..3e125716f --- /dev/null +++ b/public/images/hero/figure-svg copy.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/hero/figure-svg.svg b/public/images/hero/figure-svg.svg new file mode 100755 index 000000000..d9e69a988 --- /dev/null +++ b/public/images/hero/figure-svg.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/hero/heart-svg.svg b/public/images/hero/heart-svg.svg new file mode 100755 index 000000000..423372804 --- /dev/null +++ b/public/images/hero/heart-svg.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/hero/hero-background-svg.svg b/public/images/hero/hero-background-svg.svg new file mode 100755 index 000000000..1ed75aa6d --- /dev/null +++ b/public/images/hero/hero-background-svg.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/hero/hero-mask-svg.png b/public/images/hero/hero-mask-svg.png new file mode 100755 index 000000000..011e0f506 Binary files /dev/null and b/public/images/hero/hero-mask-svg.png differ diff --git a/public/images/hero/hero-mask-svg.svg b/public/images/hero/hero-mask-svg.svg new file mode 100755 index 000000000..c89fa6a52 --- /dev/null +++ b/public/images/hero/hero-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/hero/popup-thumb.png b/public/images/hero/popup-thumb.png new file mode 100755 index 000000000..46799f0f6 Binary files /dev/null and b/public/images/hero/popup-thumb.png differ diff --git a/public/images/hero/signatureupdate.gif b/public/images/hero/signatureupdate.gif new file mode 100755 index 000000000..aef8e030f Binary files /dev/null and b/public/images/hero/signatureupdate.gif differ diff --git a/public/images/latest-post/latest-post-1.png b/public/images/latest-post/latest-post-1.png new file mode 100755 index 000000000..88c824d3a Binary files /dev/null and b/public/images/latest-post/latest-post-1.png differ diff --git a/public/images/latest-post/latest-post-2.png b/public/images/latest-post/latest-post-2.png new file mode 100755 index 000000000..06b32a345 Binary files /dev/null and b/public/images/latest-post/latest-post-2.png differ diff --git a/public/images/latest-post/latest-post-3.png b/public/images/latest-post/latest-post-3.png new file mode 100755 index 000000000..ae6f1ded7 Binary files /dev/null and b/public/images/latest-post/latest-post-3.png differ diff --git a/public/images/pin.png b/public/images/pin.png new file mode 100755 index 000000000..2ccf076bf Binary files /dev/null and b/public/images/pin.png differ diff --git a/public/images/portfolio/ProsperLoan.html b/public/images/portfolio/ProsperLoan.html new file mode 100755 index 000000000..f4e2d6ead --- /dev/null +++ b/public/images/portfolio/ProsperLoan.html @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+

PROSPER LOAN

+
+
+

by MAY CHU

+

========================================================

+
+
+

ABSTRACT

+

This project aims to use Exploratory Data Analysis (EDA) to explore the Prosper Loan dataset which includes over 113937 listings of Prosper borrowers.

+

Prosper is one of the leading online Peer-to-peer lending platform that connects potential borrowers with potential lenders, filters candidates on both sides of the equation, and sets some rules. Peer-to-peer lending is the new trend in recent years, which allows people to seek out a loan from somewhere other than a traditional banking institution. Instead of going to a bank or a credit union, borrowers can get a loan that comes from individuals looking to earn more interest on their money than promised by traditional savings accounts.

+

Here how it works:

+
    +
  • Borrowers choose a loan amount, purpose and post a loan listing.
  • +
  • Investors review loan listings and invest in listings that meet their criteria.
  • +
  • Once the process is complete, borrowers make fixed monthly payments and investors receive a portion of those payments directly to their Prosper account.
  • +
+

This project tends to answer 4 questions:

+
    +
  1. What is the population of Prosper borrowers?
  2. +
  3. What factors affect the number of investors to prosper loan?
  4. +
  5. What affect borrower rate and APR?
  6. +
  7. Loss in charge-off loans?
  8. +
+
+
+

Univariate Plots Section

+
+

A. Listing summary

+
+

I. Loan term

+
## Source: local data frame [3 x 2]
+## 
+##    Term     n
+##   (int) (int)
+## 1    12  1614
+## 2    36 87778
+## 3    60 24545
+
## [1] 0.7704082
+

There are only 3 loan terms in the dataset: 12,36,60 months. Loans with duration of 36 months (3 years) is the most popular with 87,778 observations (account for 77%).

+
+
+

II. Loan original amount

+

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1000    4000    6500    8337   12000   35000
+
## [1] 430
+
## [1] 867
+

75% of the loans smaller than $12000, 50% lie between $4000 to $12000. The maximum loan amount Prosper allows is $35,000. There are 430 loans with the amount of $350,000. Not all loans are fully funded, there are 867 partially funded loans. The status of partial funded loans are as the chart below

+

+
+
+

III. Loan purpose

+

+

There are 20 loans purpose: 0 - Not Available, 1 - Debt Consolidation, 2 - Home Improvement, 3 - Business, 4 - Personal Loan, 5 - Student Use, 6 - Auto, 7- Other, 8 - Baby&Adoption, 9 - Boat, 10 - Cosmetic Procedure, 11 - Engagement Ring, 12 - Green Loans, 13 - Household Expenses, 14 - Large Purchases, 15 - Medical/Dental, 16 - Motorcycle, 17 - RV, 18 - Taxes, 19 - Vacation, 20 - Wedding Loans

+

The most common purpose is for Debt consolidation (50%), about 15% of borrowers don’t specify their purpose.

+
+
+

IV. Estimated Performance of the loan

+
+
1.Prosper rating
+

The Prosper Rating assigned at the time the listing was created between HR (worst) - AA (best). Applicable for loans originated after July 2009. Prosper Ratings allow potential investors to easily consider a loan application’s level of risk because the rating represents an estimated average annualized loss rate range to the investor.

+

+
## Source: local data frame [8 x 2]
+## 
+##   ProsperRating..Alpha.     n
+##                  (fctr) (int)
+## 1                    HR  6935
+## 2                     E  9795
+## 3                     D 14274
+## 4                     C 18345
+## 5                     B 15581
+## 6                     A 14551
+## 7                    AA  5372
+## 8                    NA 29084
+

Prosper rating of C is the most count (represent 16%). Only 5000 borrowers have prosper rating of AA (which is the least count). 26% of the dataset (29084 listings) has blank Prosper Rating. As we know that Prosper Rating was just introduced after 2009. Before that, Prosper used the Credit Grade as the reference for borrowers’ credit. The distribution of Credit Grade is shown in the plot below.

+

+
+
+
+

2. Lender’s yield, Estimated Effective Yield, Estimated Loss and Estimated Return

+

+

Lender yield = interest rate - servicing fee Lender yield clusters around 0.05 to 0.3 (5% - 30%)

+

Effective yield = borrower interest rate - the servicing fee rate - estimated uncollected interest on charge-offs + estimated collected late fees. Most Effective yield fluctuates around 0.1 to 0.3 (10%-30%)

+

Estimated loss is the estimated principal loss on charge-offs. Most of its values get around 0 to 0.2% (0-20%)

+

Estimated return = Estimated Effective Yield - Estimated Loss Rate. Estimated return value clusters around 10%

+
+
+
+

B. Borrower’s information

+
+

I. Borrower’s location

+

+

15000 borrowers come from California, which rank it the first place among borrower origin states. States of more than 5000 borrowers include: Georgia, Illinois, Florida, New York and Texas

+
+
+

II. Occupation

+

+

The are 68 occupations in the list. Many borowers don’t specify their occupations (with category Other). There are also missing values

+
+
III. Employment status
+

+

Most of the borrowers are employed (60%), work full-time(20%),part-time (2%) and self-employed (5%)… There are only 835 borrowers unemployed (very small percentage). There are missing values, Other and Not available answer which make it difficult to determine these borrowers’ employment status.

+
+
+
Employment status duration
+

Employment status duration is the length in months of the employment status at the time the listing was created.

+

+

To have a more general view of employment duration, I will join the duration by years by bucket

+
## 
+##   (0,5]  (5,10] (10,15] (15,20] (20,25] (25,30] (30,35] (35,40] 
+##   48076   25624   14194    7362    4449    2715    1408     604
+

+

From the chart, we can see that nearly 5000 borrowers have stayed in their current status from 1 to 5 years. However, knowing this number doesn’t make any sense if we don’t know which status they are in. So, I will color the employment status against this bar chart

+

+

It’s clear that most borrowers are employed and full-time employed. Over 30,000 borrowers have been employed from 1-5 years. Unemployed borrowers only stay in that status for less than 5 years.

+
+
+
+

IV. Income Range

+

+
##   Not employed  Not displayed             $0      $1-24,999 $25,000-49,999 
+##            806           7741              0           7274          32192 
+## $50,000-74,999 $75,000-99,999      $100,000+           NA's 
+##          31050          16916          17337            621
+

Over 60000 borrowers earn from 25000 to 75000 annually (account for half of the dataset)

+
+
Income Range vs. Income Verifiable
+

+

Only a very small number of borrowers cannot verify income. The only exception appears in Not-employed group, but the reason is quite obvious.

+
+
+
+

V. Homeownership

+
## Source: local data frame [2 x 2]
+## 
+##   IsBorrowerHomeowner     n
+##                 (lgl) (int)
+## 1               FALSE 56459
+## 2                TRUE 57478
+

The number of borrowers who own a house is similar to those who don’t own the house. It seems having a collateral doesn’t affect a Prosper loan listing

+
+
+

V. Borrower’s Credit Information

+
+
1. Prosper Score
+

Prosper Score is a custom risk score built using historical Prosper data. The score ranges from 1-11, with 11 being the best, or lowest risk score. Applicable for loans originated after July 2009.

+

+

The most common Prosper Score is 4, 6, 8 (account for nearly 15% each)

+
+
+
2.Credit Score
+

+

Similar to prosper rating and prosper score, many borrowers have medium credit score range from 640 to 779. Very few have scores lower than 640 or higher than 780.

+
+
+
3.Debt/Income Ratio
+

The debt to income ratio of the borrower at the time the credit profile was pulled. This value is Null if the debt to income ratio is not available. This value is capped at 10.01 (any debt to income ratio larger than 1000% will be returned as 1001%).

+

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
+##   0.000   0.140   0.220   0.276   0.320  10.010    8554
+
## [1] 272
+

There are 272 outliers with debt/income ratio = 1001%. However, most borrowers have debt to income ratio smaller than 0.5. The most occurrence is around 0.1 to 0.25.

+
+
+
4. Credit history
+

Current Deliquencies: Number of accounts delinquent at the time the credit profile was pulled.

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
+##  0.0000  0.0000  0.0000  0.5921  0.0000 83.0000     697
+

+

87.7% Prosper Borrowers (100,000) have no current delinquenct accounts at the time they created the listing.

+

Inquiries Last 6 Months: Number of inquiries in the past six months at the time the credit profile was pulled. Inquiries are the number of times borrowers attempted to borrow money in the past six months

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
+##   0.000   0.000   1.000   1.435   2.000 105.000     697
+

+

Bank card utilization: The percentage of available revolving credit that is utilized at the time the credit profile was pulled.

+

+

There are nearly 7000 borrowers with 0 bank card utilization. However, among 99,551 (87.4%) borrowers who use their credit cards, there are 63% have the bank card utilization more than 50%. There are 1744 borrowers whose bank card utilization larger than 100%.

+
+
+
6.Prior Prosper Loan Performance
+
## [1] 22084
+
## [1] 0.1938264
+

+
## Source: local data frame [10 x 2]
+## 
+##    TotalProsperLoans     n
+##                (int) (int)
+## 1                  0     1
+## 2                  1 15538
+## 3                  2  4540
+## 4                  3  1447
+## 5                  4   417
+## 6                  5   104
+## 7                  6    29
+## 8                  7     8
+## 9                  8     1
+## 10                NA 91852
+

There are 22084 borrowers have prior prosper loans, which account for 19%. Among them, 70% have only 1 prior prosper loans

+
## Source: local data frame [3 x 2]
+## 
+##   prior.prosper.performance < 1     n
+##                           (lgl) (int)
+## 1                         FALSE 18214
+## 2                          TRUE  3806
+## 3                            NA 91917
+
## 
+##     FALSE      TRUE 
+## 0.8271571 0.1728429
+

Among 22084 borrowers who have prior prosper loans, there are 18214 borrowers pay on time (82.7%), 3806 (17.3%) borrowers have overdue

+
+
+
+
+

C. Loan Activity

+
+

I. Loans actual performance

+

Loan status

+

+

There are over 56576 Current loans (50%), over 38074 completed loans (35%) . 19077 loans have delinquent which account for 16,7% consisting of: 2067 cases deliquent from 1 to 120 days past-due, 5018 default (121+ days past due) and 11992 Chargeoff loans (150+ days past dues, no expectation of sufficient payment)

+
## [1] 16902
+
## [1] 0.1483451
+

Number of Gross principal Loss cases: 16902 account for 14%

+
+
+

II. Borrower rate (Interest)

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##  0.0000  0.1340  0.1840  0.1928  0.2500  0.4975
+

+

Mean interest rate is 19.28%. Haft of the rate is about 13.4% to 25%. The distribution is quite bimodal, which two modes of frequent interest rate: from 14-16% to 31-32%, which is pretty high.

+

Do the borrower rate diffirent with different original loan quarter? To discover this, I’ll graph the mean of borrower rate against loan origination quarter and year

+

+
+
+

III. Borrower APR

+

Borrower APR is the Borrower’s Annual Percentage Rate (APR) for the loan. APR refers to the total cost of borrowing, as the calculation for APR includes not only the interest rate, but also many other fees the borrower might be charged. So APR is seen as the “effective interest rate,” a way for borrowers to compare one loan to another.

+

+

The Borrower APR distribution looks similar to that of borrower rate, although the histogram shifts rightward, indicating that the APR is generally larger than interest rate. But how large is the difference?

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
+##   0.000   1.900   2.502   2.604   3.653  14.930      25
+

+

As the APR is equal to interest rate plus fees. The most common fees are around 0.5-1%, 2-3% and 3.5-4%. What cause the difference in fees?

+
+
+

IV. Investors

+
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    2.00   44.00   80.48  115.00 1189.00
+

+

75% of loans have less than 115 investors. There are outliers, however, some loans have more than 1000 investors. I wonder what make these loans so attractive. The distribution of investor counts is right-skewed. To make it normal, I’ll transform it to log10 to reduce skewness

+

+
## Source: local data frame [2 x 2]
+## 
+##   Investors == 1     n
+##            (lgl) (int)
+## 1          FALSE 86123
+## 2           TRUE 27814
+

There are over 27000 loans with only 1 investors, I will cut that out of the histogram to have a better feel of investor distribution

+

+
+
+
+
+

Univariate Analysis

+
+

What is the structure of your dataset?

+

This data set contains 113,937 loans with 81 variables on each loan. The variables can be classified into 3 categories: 1. Listing summary: the summary of information about the loan application based on which lenders can make investing decisions (prosper rating, loan term, loan amount, loan purpose and estimated performance of a loan such as estimated effective yield, estimated lender yield, estimated loss rate and estimated return rate) 2. Borrower’s information: borrowers’ geography, employment status and status duration, prosper score, borrower’s credit history such as credit score, prosper score, number of credit lines and current/past delinquencies… 3. The loan activity: the status of loans, number of investors, the borrower rate/APR and the loans’ performance

+
+
+

What is/are the main feature(s) of interest in your dataset?

+
    +
  • The number of investors participate in a loan
  • +
  • The interest rate (borrower rate) and APR of the loan
  • +
  • The underperformed loans (charge-off loans)
  • +
+
+
+

What other features in the dataset do you think will help support your investigation into your feature(s) of interest?

+
    +
  • What factors deciding the borrower rate and APR of a loan: prosper rating, estimated loss rate and loan term…
  • +
  • What attract investors to a loan: I suspect that investor may look at prosper rating, lender yield, return yield, employment status, debt to income ratio, credit rating… to decide whether a loan is rewarding
  • +
  • Common features of underperfomed loans: prosper rating, debt to income ratio, delinquencies history, proser loan history or public record…
  • +
+
+
+

Did you create any new variables from existing variables in the dataset?

+

I created several new variables - difference.in.rate: show the difference between borrower APR and borrower rate - duration.by.bucket: join the duration of employment status duration into different intervals - rate.vs.time: group the dataset by quarter and year to compare the borrower’s rate - CreditRange: a range of lower and upper Credit Score - prior.prosper.performance: the performance of the prior prosper loan, which represent the ratio of on time payment on total billed payments. - CurrentDelinquence.bucket, Delinquence7year.bucket, Inquiries6month.bucket

+
+
+

Of the features you investigated, were there any unusual distributions? Did you perform any operations on the data to tidy, adjust, or change the form of the data? If so, why did you do this?

+

The investor distribution is right-skewed. So I take the log10 of the number of investors to create a more normal distribution. I hope to build a regression model for this variable later.

+
+
+
+

Bivariate and Multivariate Plots Section

+
+

I. Prosper Rating & Estimated Loss Rate

+

Prosper Rating represents an estimated average annualized loss rate range to the investor. The estimated loss rate is based on the historical performance of Prosper loans with similar characteristics.

+

+

Prosper Rating vs. Estimated Avg. Annual Loss Rate

+
    +
  • AA 0.00 - 1.99%
  • +
  • A 2.00 - 3.99%
  • +
  • B 4.00 - 5.99%
  • +
  • C 6.00 - 8.99%
  • +
  • D 9.00 - 11.99%
  • +
  • E 12.00 - 14.99%
  • +
  • HR > 15.00%
  • +
+
+
+

Estimated Loss by Prosper Score and Credit Range

+

+

Higher credit score, lower estimated loss. Estimated loss’ means drops with the increase in credit range. The same phenomenon can be observed in the effect of Prosper Score on Estimated loss

+

+

+

Clearly, mean estimated loss goes down with the increase in Prosper Rating and Credit Range, which shows a strong negative linear relationship.

+
+
+

Prosper Score

+

The Prosper score estimates the probability of a loan going “bad,” where “bad” is the probability of going 60+ days past due within the first twelve months from the date of loan origination. Applicants from April, 2008 through April, 2011 were used to build the discrete additive scorecard. The scorecard was verified and results validated on an independent validation sample of loans booked during the same time period as well as a more recent sample of loans booked outside of the loan development period.

+

Key variables in the scorecard are: - Number of inquiries on the credit bureau: InquiriesLast6Months - Number of delinquent accounts on the credit bureau: CurrentDelinquencies - Credit card utilization on the credit bureau: BankcardUtilization - Number of recently opened trades on the credit bureau: TradesOpenedLast6Months - Debt to income ratio: DebtToIncomeRatio - Loan payment performance on prior Prosper loans

+

+

+

+

+

Trying to build a sub dataset from April 2008 to April 2011 and graphing the key variables in scorecard against the Prosper Score, I can see that Prosper Score has negative relationship with inquiries last 6 month, current delinquencies, bankcard utilization and debt to income ratio… However, this relationship is not clear and hard to quantify.

+
+
+

II. What affect the loan interest rates

+
+

Prosper rating and Credit Grade on Borrower Rate

+

+

Clearly, Borrower Rate decreases when Prosper Rating increases. Before July 2009, Prosper used Credit Grade to calculate Borrower Rate. I’ll use graph Borrower Rate on Credit Grade for loan from 2005 to 2009

+

+

Although the same range of Borrower rate is assigned to the same level of credit grade and prosper rating, the fundamental difference is the way credit grade and prosper rating determined. Credit Grade is based only on credit bureau score (AA: 760+, HR: 520-559) Prosper Rating takes in various credit information of borrowers including prior prosper loans performance

+
+
+

Estimated Loss on Borrower Rate

+

+
## [1] 0.945297
+

When estimated loss increases, borrower rate also increases and the borrower rate becomes more disperse. The correlation between estimated loss and borrower rate is high 94.5%, showing a strong and positive correlation

+
+
+

Estimated Loss on Borrower Rate color by Prosper Rating

+

+
+
+

Estimated Loss on Borrower Rate, facet by Term

+

+

Borrower Rate is positively correlated with estimated loss. Higher prosper rating borrowers have lower borrower rate. We can see clearly that different prosper rating is associated with a specific borrower rate. Loan with term of 36 months have borrower rate most dispersed, maybe because most loans have term of 36 months. The other terms show perfect correlation.

+
+
+

What affect the difference between borrower rate and borrower APR

+

+
## dt$Term: 12
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   0.935   5.728   7.772   6.614   7.945  14.930 
+## -------------------------------------------------------- 
+## dt$Term: 36
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
+##   0.000   1.423   2.816   2.592   3.708   6.395      25 
+## -------------------------------------------------------- 
+## dt$Term: 60
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   0.215   2.319   2.408   2.385   2.524   2.798
+

It’s clear that the difference between borrower rate and APR is determined by term. 50% of Loans with duration of 1 year has range from 5.7% to 7.9%; 3 years from 1.423 to 3.7%; and 5 years from 2.3 to 2.5%. This difference also fluctuates with Prosper Rating. We can see that only loans with high Prosper Rating can apply for term of 5 years from the chart below

+

+
## dt$ProsperRating..Alpha.: HR
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   2.210   3.746   4.027   3.874   4.027   6.395 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: E
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   2.174   3.631   3.891   3.721   3.948  10.080 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: D
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   2.131   2.604   3.542   3.416   3.825   9.927 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: C
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   2.095   2.440   3.393   3.169   3.722   9.835 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: B
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   1.376   2.321   2.730   2.958   3.626   8.430 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: A
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   1.340   2.252   2.788   2.597   2.819   7.674 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: AA
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##   0.215   0.343   1.336   1.092   1.346   3.562
+

+
+
+
+

Number of investors sponsoring a loan

+

What draws investors to a loan

+

+

From this chart, I can see very good correlation among borrower rate, borrower APR, lender yield, effective yield and estimated return. This is obvious because these rates are calculated from each other. For example, lender’s yield is equal borrower rate less servicing fee. However, what astounds me the most is that these variables have very weak correlation with the investor count. For instance, correlation between lender’s yield and investors is only -0.249. This is beyond my expectation.

+

When continuous variables show little effect on investor number, I’ll try to graph the investor number against nominal variables such as prosper rating, income range, although relationship between nominal and continuous variables can be difficult to quantify.

+

Prosper Rating on Investor counts:

+

+
## dt$ProsperRating..Alpha.: HR
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    9.00   31.00   35.28   54.00  237.00 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: E
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    2.00   25.00   35.01   55.00  279.00 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: D
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    4.00   35.00   56.23   87.00  511.00 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: C
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    1.00    9.00   51.09   78.00 1024.00 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: B
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##     1.0     1.0    19.0    69.8   112.0   856.0 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: A
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00    1.00   51.00   97.53  162.00 1189.00 
+## -------------------------------------------------------- 
+## dt$ProsperRating..Alpha.: AA
+##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
+##    1.00   62.75  150.00  178.40  274.00 1035.00
+

The better Prosper Rating, the more investors. For example: for rating AA, 50% of Investor count range from 63 to 274 with mean 150. For rating HR, 50% of Investor count range from 9 to 54 with mean 35 investors.

+

Income Range on Investor counts

+

+

Investors number get a bit higher with the increase in income range. The same thing can be observed between investor number and debt to income ratio

+

Debt to Income Ratio on Investor count

+

+

Borrowers who have debt to income ratio lower than 1 attract most investors (risk averse). However, there’re investors who accept very high debt to income ratio of 10.01 (which is larger than 1000 percent). Also this relationship is not linear, which explains why our correlation coefficient is very poor (0.00176)

+

Lender Yield on Investor count

+

+

Investors don’t prefer very high lender yield because high lender yield means high borrower rate, means high risk. That’s why we can see the number of investors go down when lender yield increase. Most preferred rate is around 10%

+

+

Estimated return on Investor count

+

+

Similar to lender yield, investors prefer estimated return around 10%. When they invest in lower Prosper Rating loans, they expect higher return (10-20%). But there are also investors accept negative estimated returns and invest in HR loans.

+

Loans with low and high number of investors:

+

+

The popular rating for loans with only 1 investors is C with 8000 loans.The popular rating for loans with more than 100 investors are A with approximately 5500 counts.

+
+
+

Underperformed loans

+

This section we will work with underperformed loans (charged-off loans) and discover their common patterns

+
## [1] 11992
+
## [1] 0.1052511
+

There are 11992 charged off loans (account for 10.5% )

+

+

Most borrowers with chargeoff loss have Prosper Rating lower than D and estimated loss larger than 10%.

+

Performance of charged off loan

+

+
## 
+##     FALSE      TRUE 
+## 0.1882922 0.8117078
+

For both Gross Principal Loss and Net Principal Loss, the thickest area lies from 50% to 100% and under 100 investors. Nearly 81% of Net percentage loss is over 50%.

+

Facet the chart using Prosper Rating, we see how Prosper Rating affect the investors number to a loan (a pattern we have discovered earlier). Low investor count usually go with low Prosper rating, and those loans with low ratings tend to have high percentage loss when it comes to charge off loans

+

+

There are some cases, however, that the investors can recover of their investments. In those cases, Net Principal Loss is smaller than Gross Principal Loss. We can observe the phenomenon more clearly in the chart below where many points lie below the equate line of gross principal loss and net principal loss

+

+

This brings me to a question of how accurate the estimated loss Prosper calculated in comparison to the actual loss investors have to suffer

+

+

Sadly, the forecast is not even close. The actual loss is much higer than the estimated loss that Prosper uses to determine Prosper Rating based on such investors may make their decision.

+
+
+
+

Bivariate and Multivariate Analysis

+
+

Talk about some of the relationships you observed in this part of the investigation. How did the feature(s) of interest vary with other features in the dataset?

+

We learn from Loan purpose that most borrowers come to Prosper to find a means to settle their debt. This explains why Bank card utilization of most borrowers is high, and most of them don’t have very good prosper score, credit range as well as prosper rating.

+

Prosper uses Prosper Score and Credit Score to determine the Estimated Loss Rate. Estimated Loss Rate is then employed to calculate the Borrower Rate and to determine Prosper Rating. Term affects Borrower APR when Prosper imposes different service fee on different loan terms borrowers apply for.

+

Lender yield, Estimated effective yield and Estimated return are all derived from Borrower Rate. That’s why these rates show high correlation to each other. And because they are inter-dependent, it’s not useful to throw them together to predict effect on another variable (for example: investor count). At best, we can only use one of them as the predictor (lender yield or borrower rate, estimated loss rate). However, the correlation between such rate and investor count is negative and weak (~ -0.3%). This can infer that lenders participating to Prosper Loans are quite risk adverse. Still I cannot find any factors which affect investors’ decision strongly. Prosper Rating is a good indicator, but not a dominant factor. Hence, I think investor’s choice to invest in a loan is quite intuitive.

+

When investigating charged-off loans, I also realize that the estimated loss rate will not accurately predict the actual loss rate. Therefore, investors should be careful to base their decisions on such information.

+
+
+

What was the strongest relationship you found?

+

The strongest relationship I’m able to observe in this dataset is between estimated loss rate and borrower rate. Hence, I will build linear model to predict borrower rate based on estimated loss rate

+
+
+
+

Prediction Model

+
+

Linear Model of Borrower Rate

+

Now I will build a model to predict the Borrower Rate based on Estimated Loss Rate as they show good correlation to each other. As Estimated Loss was only calculated after July 2009, I will predict based on a sub dataset after July 2009

+
## 
+## Calls:
+## m1: lm(formula = BorrowerRate ~ EstimatedLoss, data = dtAfter2009)
+## 
+## ===========================
+## (Intercept)       0.075*** 
+##                  (0.000)   
+## EstimatedLoss     1.509*** 
+##                  (0.002)   
+## ---------------------------
+## R-squared             0.894
+## adj. R-squared        0.894
+## sigma                 0.024
+## F                712518.799
+## p                     0.000
+## Log-likelihood   194862.855
+## Deviance             50.292
+## AIC             -389719.710
+## BIC             -389691.664
+## N                 84853    
+## ===========================
+

So let assume a listing has Estimated Loss of 0.0049, what is the estimated value of borrower rate given confidence level of 95%

+
##          fit        lwr       upr
+## 1 0.08226263 0.03454436 0.1299809
+

Borrower Rate of this listing has mean value of 0.08, and 95% chance that the rate will be from 0.035 to 0.13. 89.4% variability in Borrower Rate can be explained by that in Estimated Loss. This model only uses Estimated Loss as the predictor because (1) it has good correlation coefficient with Investor counts (2) as Estimated loss is derived from many other variables in the dataset (Prosper Score, Credit Range, borrowers’ credit history, Prosper Rating), throwing those variables into the model can cause multicollinearity.

+
+
+

Multilinear Model of Estimated Loss

+
## 
+## Calls:
+## l1: lm(formula = EstimatedLoss ~ ProsperScore, data = dtAfter2009)
+## l2: lm(formula = EstimatedLoss ~ ProsperScore + CreditRange, data = dtAfter2009)
+## 
+## =====================================================
+##                                   l1          l2     
+## -----------------------------------------------------
+## (Intercept)                     0.159***    0.191*** 
+##                                (0.000)     (0.001)   
+## ProsperScore                   -0.013***   -0.011*** 
+##                                (0.000)     (0.000)   
+## CreditRange: 620-639/600-619               -0.007*** 
+##                                            (0.001)   
+## CreditRange: 640-659/600-619               -0.020*** 
+##                                            (0.001)   
+## CreditRange: 660-679/600-619               -0.033*** 
+##                                            (0.001)   
+## CreditRange: 680-699/600-619               -0.041*** 
+##                                            (0.001)   
+## CreditRange: 700-719/600-619               -0.049*** 
+##                                            (0.001)   
+## CreditRange: 720-739/600-619               -0.054*** 
+##                                            (0.001)   
+## CreditRange: 740-759/600-619               -0.057*** 
+##                                            (0.001)   
+## CreditRange: 760-779/600-619               -0.060*** 
+##                                            (0.001)   
+## CreditRange: 780-799/600-619               -0.065*** 
+##                                            (0.001)   
+## CreditRange: 800-819/600-619               -0.067*** 
+##                                            (0.001)   
+## CreditRange: 820-839/600-619               -0.066*** 
+##                                            (0.001)   
+## CreditRange: 840-859/600-619               -0.068*** 
+##                                            (0.002)   
+## CreditRange: 860-879/600-619               -0.067*** 
+##                                            (0.003)   
+## CreditRange: 880-899/600-619               -0.065*** 
+##                                            (0.008)   
+## -----------------------------------------------------
+## R-squared                           0.454       0.545
+## adj. R-squared                      0.454       0.545
+## sigma                               0.035       0.032
+## F                               70527.597    6764.427
+## p                                   0.000       0.000
+## Log-likelihood                 165141.180  172849.094
+## Deviance                          101.331      84.497
+## AIC                           -330276.360 -345664.187
+## BIC                           -330248.314 -345505.260
+## N                               84853       84853    
+## =====================================================
+
+
+
+
+

Final Plots and Summary

+
+

Plot One: Estimated Loss by Prosper Score and Credit Range

+

+
+
+

Description One

+

This plot shows the effect of Prosper Score and Credit Range on Mean estimated loss. Estimated loss is higher with lower Prosper Score and Credit Range.

+
+
+

Plot Two:

+

Borrower Rate and Borrower APR by Estimated Loss and Term

+

+
+
+

Description Two

+

The second plot answers the question of what influence the Borrower Rate and Borrower APR. Since we can see the good linear relationship between Estimated Loss and Borrower Rate, as well as how Term affects the difference between Borrower Rate and APR, we have a better idea of what Prosper will look at when determining Borrower Rate and Borrower APR

+
+
+

Plot Three:

+

+
+
+

Description Three

+

Watch out!!! Forecast is just forecast. The estimated loss rate is much lower than the actual loss rate when it comes to charge-off loans. As Prosper uses estimated loss rate to determine Prosper Rating, borrower rate and consequently lender’s yield as well as estimated return; lenders who merely base their decision on those factors are risking losing more than what they expect.

+
+
+
+
+

Reflection

+

There are 81 variables in this dataset, but the variables used to make lending decisions are boiled down to several ones including Prosper Rating and Estimated Loss Rate; Prosper Score and Credit Range; Lender yield, estimated effective yield and estimated return… I wonder where borrowers’ information such as credit history, bankcard utilizaton, delinquencies,.. go into this process?

+

Prosper claims that they use a discrete scorecard built on applications from April 2008 to April 2011 to determine the Prosper Score. The scorecard takes in variables of borrowers’ credit information. However, graphing the Prosper Score against these variables, I see negative correlation but not enough to quantify. Lacking of domain knowledge, I was unable to build and test the effectiveness of this scorecard in determining Prosper Score, which in turn affects Estimated Loss Rate. Keeping in mind that Estimated Loss Rate is then used to decide Borrower Rate and Prosper Rating. The accuracy of the score card is critical to enhance Prosper’s ability to estimate yield and return for investors. However, from the dataset we can see that the accuracy of estimated loss rate is very poor. Investors should be careful when using these factors in their decision making process.

+

I didn’t build a model to predict investor number, because the correlation of various factors on investor counts is weak. Building a model with such low R square can be ineffective. Instead, I build a linear model to determine Estimated Loss Rate with good R square of 89.9%.

+
+ + +
+ + + + + + + + diff --git a/public/images/portfolio/case-details.png b/public/images/portfolio/case-details.png new file mode 100755 index 000000000..14c6d9618 Binary files /dev/null and b/public/images/portfolio/case-details.png differ diff --git a/public/images/portfolio/deep_learning.jpeg b/public/images/portfolio/deep_learning.jpeg new file mode 100755 index 000000000..f4d52473f Binary files /dev/null and b/public/images/portfolio/deep_learning.jpeg differ diff --git a/public/images/portfolio/predictchurn.jpeg b/public/images/portfolio/predictchurn.jpeg new file mode 100755 index 000000000..d309f7fe2 Binary files /dev/null and b/public/images/portfolio/predictchurn.jpeg differ diff --git a/public/images/portfolio/seattle.jpeg b/public/images/portfolio/seattle.jpeg new file mode 100755 index 000000000..223d5db56 Binary files /dev/null and b/public/images/portfolio/seattle.jpeg differ diff --git a/public/images/portfolio/stockprice.jpeg b/public/images/portfolio/stockprice.jpeg new file mode 100755 index 000000000..3026f9611 Binary files /dev/null and b/public/images/portfolio/stockprice.jpeg differ diff --git a/public/images/screenshots/aks.png b/public/images/screenshots/aks.png new file mode 100755 index 000000000..fd568bf6a Binary files /dev/null and b/public/images/screenshots/aks.png differ diff --git a/public/images/screenshots/debezium-connect-cluster.png b/public/images/screenshots/debezium-connect-cluster.png new file mode 100755 index 000000000..e7a1f4d86 Binary files /dev/null and b/public/images/screenshots/debezium-connect-cluster.png differ diff --git a/public/images/screenshots/load-to-df.png b/public/images/screenshots/load-to-df.png new file mode 100755 index 000000000..0bbe9c544 Binary files /dev/null and b/public/images/screenshots/load-to-df.png differ diff --git a/public/images/screenshots/python.png b/public/images/screenshots/python.png new file mode 100755 index 000000000..119f5ea3a Binary files /dev/null and b/public/images/screenshots/python.png differ diff --git a/public/images/screenshots/scala-solution.png b/public/images/screenshots/scala-solution.png new file mode 100755 index 000000000..0adbbbb36 Binary files /dev/null and b/public/images/screenshots/scala-solution.png differ diff --git a/public/images/service/background-pattern copy.svg b/public/images/service/background-pattern copy.svg new file mode 100755 index 000000000..ed66856c0 --- /dev/null +++ b/public/images/service/background-pattern copy.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/service/background-pattern.svg b/public/images/service/background-pattern.svg new file mode 100755 index 000000000..be26757a7 --- /dev/null +++ b/public/images/service/background-pattern.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/service/background-shape.svg b/public/images/service/background-shape.svg new file mode 100755 index 000000000..33cd235cd --- /dev/null +++ b/public/images/service/background-shape.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/service/branding.svg b/public/images/service/branding.svg new file mode 100755 index 000000000..52ece1d7f --- /dev/null +++ b/public/images/service/branding.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/service/creative.svg b/public/images/service/creative.svg new file mode 100755 index 000000000..ec038caa8 --- /dev/null +++ b/public/images/service/creative.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/images/service/ui-ux.svg b/public/images/service/ui-ux.svg new file mode 100755 index 000000000..71a689fa1 --- /dev/null +++ b/public/images/service/ui-ux.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/service/web.svg b/public/images/service/web.svg new file mode 100755 index 000000000..cda4a4d41 --- /dev/null +++ b/public/images/service/web.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/single-blog/aksdbz.png b/public/images/single-blog/aksdbz.png new file mode 100755 index 000000000..b8eba3e13 Binary files /dev/null and b/public/images/single-blog/aksdbz.png differ diff --git a/public/images/single-blog/blog-img.jpg b/public/images/single-blog/blog-img.jpg new file mode 100755 index 000000000..69daf74f0 Binary files /dev/null and b/public/images/single-blog/blog-img.jpg differ diff --git a/public/images/single-blog/data-lakehouse.png b/public/images/single-blog/data-lakehouse.png new file mode 100755 index 000000000..56b286d99 Binary files /dev/null and b/public/images/single-blog/data-lakehouse.png differ diff --git a/public/images/single-blog/feature-image.jpg b/public/images/single-blog/feature-image.jpg new file mode 100755 index 000000000..db07a6598 Binary files /dev/null and b/public/images/single-blog/feature-image.jpg differ diff --git a/public/images/single-blog/fireworks.gif b/public/images/single-blog/fireworks.gif new file mode 100755 index 000000000..f4352adf0 Binary files /dev/null and b/public/images/single-blog/fireworks.gif differ diff --git a/public/images/single-blog/fy22/1flipfest.webp b/public/images/single-blog/fy22/1flipfest.webp new file mode 100755 index 000000000..d7b493c01 Binary files /dev/null and b/public/images/single-blog/fy22/1flipfest.webp differ diff --git a/public/images/single-blog/fy22/colchuck-winter3.webp b/public/images/single-blog/fy22/colchuck-winter3.webp new file mode 100755 index 000000000..be3259832 Binary files /dev/null and b/public/images/single-blog/fy22/colchuck-winter3.webp differ diff --git a/public/images/single-blog/fy22/colchuck.webp b/public/images/single-blog/fy22/colchuck.webp new file mode 100755 index 000000000..bd574932f Binary files /dev/null and b/public/images/single-blog/fy22/colchuck.webp differ diff --git a/public/images/single-blog/fy22/google-cloud.webp b/public/images/single-blog/fy22/google-cloud.webp new file mode 100755 index 000000000..48fcd69cb Binary files /dev/null and b/public/images/single-blog/fy22/google-cloud.webp differ diff --git a/public/images/single-blog/fy22/lake22.webp b/public/images/single-blog/fy22/lake22.webp new file mode 100755 index 000000000..6b3b10529 Binary files /dev/null and b/public/images/single-blog/fy22/lake22.webp differ diff --git a/public/images/single-blog/fy22/painting.webp b/public/images/single-blog/fy22/painting.webp new file mode 100755 index 000000000..e1cda551a Binary files /dev/null and b/public/images/single-blog/fy22/painting.webp differ diff --git a/public/images/single-blog/fy22/paradise.webp b/public/images/single-blog/fy22/paradise.webp new file mode 100755 index 000000000..b170e0d5e Binary files /dev/null and b/public/images/single-blog/fy22/paradise.webp differ diff --git a/public/images/single-blog/fy22/rainier-paradise.webp b/public/images/single-blog/fy22/rainier-paradise.webp new file mode 100755 index 000000000..02e2ab22e Binary files /dev/null and b/public/images/single-blog/fy22/rainier-paradise.webp differ diff --git a/public/images/single-blog/fy22/rainier-sunrise.webp b/public/images/single-blog/fy22/rainier-sunrise.webp new file mode 100755 index 000000000..0fa415c16 Binary files /dev/null and b/public/images/single-blog/fy22/rainier-sunrise.webp differ diff --git a/public/images/single-blog/fy22/seattle-by-night.webp b/public/images/single-blog/fy22/seattle-by-night.webp new file mode 100755 index 000000000..cd20e1b05 Binary files /dev/null and b/public/images/single-blog/fy22/seattle-by-night.webp differ diff --git a/public/images/single-blog/fy22/skiing.webp b/public/images/single-blog/fy22/skiing.webp new file mode 100755 index 000000000..5242965f1 Binary files /dev/null and b/public/images/single-blog/fy22/skiing.webp differ diff --git a/public/images/single-blog/fy22/talk.webp b/public/images/single-blog/fy22/talk.webp new file mode 100755 index 000000000..e29025214 Binary files /dev/null and b/public/images/single-blog/fy22/talk.webp differ diff --git a/public/images/single-blog/fy22/talk2.webp b/public/images/single-blog/fy22/talk2.webp new file mode 100755 index 000000000..e609f32ff Binary files /dev/null and b/public/images/single-blog/fy22/talk2.webp differ diff --git a/public/images/single-blog/fy22/team-outing.webp b/public/images/single-blog/fy22/team-outing.webp new file mode 100755 index 000000000..4bebab33b Binary files /dev/null and b/public/images/single-blog/fy22/team-outing.webp differ diff --git a/public/images/single-blog/fy22/treehouse.webp b/public/images/single-blog/fy22/treehouse.webp new file mode 100755 index 000000000..070bad328 Binary files /dev/null and b/public/images/single-blog/fy22/treehouse.webp differ diff --git a/public/images/single-blog/fy22/winter-hike.webp b/public/images/single-blog/fy22/winter-hike.webp new file mode 100755 index 000000000..5f7ee1ddc Binary files /dev/null and b/public/images/single-blog/fy22/winter-hike.webp differ diff --git a/public/images/single-blog/git-reset.png b/public/images/single-blog/git-reset.png new file mode 100755 index 000000000..cce8d6958 Binary files /dev/null and b/public/images/single-blog/git-reset.png differ diff --git a/public/images/single-blog/microsoft.jpg b/public/images/single-blog/microsoft.jpg new file mode 100755 index 000000000..bc9b6e737 Binary files /dev/null and b/public/images/single-blog/microsoft.jpg differ diff --git a/public/images/single-blog/pyspark-synapse.png b/public/images/single-blog/pyspark-synapse.png new file mode 100755 index 000000000..693053923 Binary files /dev/null and b/public/images/single-blog/pyspark-synapse.png differ diff --git a/public/images/single-blog/pyspark-synapse.webp b/public/images/single-blog/pyspark-synapse.webp new file mode 100755 index 000000000..6d546a368 Binary files /dev/null and b/public/images/single-blog/pyspark-synapse.webp differ diff --git a/public/images/single-blog/query-performance.jpeg b/public/images/single-blog/query-performance.jpeg new file mode 100755 index 000000000..a4266f83b Binary files /dev/null and b/public/images/single-blog/query-performance.jpeg differ diff --git a/public/images/single-blog/sql-pool.jpeg b/public/images/single-blog/sql-pool.jpeg new file mode 100755 index 000000000..986529ac8 Binary files /dev/null and b/public/images/single-blog/sql-pool.jpeg differ diff --git a/public/images/site-navigation/logo.png b/public/images/site-navigation/logo.png new file mode 100755 index 000000000..1c96b7afd Binary files /dev/null and b/public/images/site-navigation/logo.png differ diff --git a/public/images/site-navigation/logonew.png b/public/images/site-navigation/logonew.png new file mode 100755 index 000000000..735174630 Binary files /dev/null and b/public/images/site-navigation/logonew.png differ diff --git a/public/images/skill/myskillupdate.png b/public/images/skill/myskillupdate.png new file mode 100755 index 000000000..0aaff665a Binary files /dev/null and b/public/images/skill/myskillupdate.png differ diff --git a/public/images/skill/skill-background-shape.svg b/public/images/skill/skill-background-shape.svg new file mode 100755 index 000000000..d9c945851 --- /dev/null +++ b/public/images/skill/skill-background-shape.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/skill/skill-mask-svg.svg b/public/images/skill/skill-mask-svg.svg new file mode 100755 index 000000000..71af2d707 --- /dev/null +++ b/public/images/skill/skill-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/skill/skill_image.jpg b/public/images/skill/skill_image.jpg new file mode 100755 index 000000000..a5e1e3315 Binary files /dev/null and b/public/images/skill/skill_image.jpg differ diff --git a/public/images/skill/skills.png b/public/images/skill/skills.png new file mode 100755 index 000000000..3dc6d06bd Binary files /dev/null and b/public/images/skill/skills.png differ diff --git a/public/img/Canadian Data Guy-logos.jpeg b/public/img/Canadian Data Guy-logos.jpeg new file mode 100644 index 000000000..6558ee25d Binary files /dev/null and b/public/img/Canadian Data Guy-logos.jpeg differ diff --git a/public/img/Canadian Data Guy-logos_black.png b/public/img/Canadian Data Guy-logos_black.png new file mode 100644 index 000000000..c340b1279 Binary files /dev/null and b/public/img/Canadian Data Guy-logos_black.png differ diff --git a/public/img/Canadian Data Guy-logos_transparent.png b/public/img/Canadian Data Guy-logos_transparent.png new file mode 100644 index 000000000..3cd18df1e Binary files /dev/null and b/public/img/Canadian Data Guy-logos_transparent.png differ diff --git a/public/img/Canadian Data Guy-logos_white.png b/public/img/Canadian Data Guy-logos_white.png new file mode 100644 index 000000000..f5c2814c2 Binary files /dev/null and b/public/img/Canadian Data Guy-logos_white.png differ diff --git a/public/img/apple-touch-icon-old.png b/public/img/apple-touch-icon-old.png new file mode 100644 index 000000000..b3531dd4d Binary files /dev/null and b/public/img/apple-touch-icon-old.png differ diff --git a/public/img/apple-touch-icon.png b/public/img/apple-touch-icon.png new file mode 100644 index 000000000..0e46869d4 Binary files /dev/null and b/public/img/apple-touch-icon.png differ diff --git a/public/img/avatar.png b/public/img/avatar.png new file mode 100644 index 000000000..7bea86165 Binary files /dev/null and b/public/img/avatar.png differ diff --git a/public/img/banner.jpg b/public/img/banner.jpg new file mode 100644 index 000000000..3bdfd19ab Binary files /dev/null and b/public/img/banner.jpg differ diff --git a/public/img/banner2.jpg b/public/img/banner2.jpg new file mode 100644 index 000000000..09a370ac1 Binary files /dev/null and b/public/img/banner2.jpg differ diff --git a/public/img/basketsquare.jpg b/public/img/basketsquare.jpg new file mode 100644 index 000000000..5040f3927 Binary files /dev/null and b/public/img/basketsquare.jpg differ diff --git a/public/img/blog-avatar.jpg b/public/img/blog-avatar.jpg new file mode 100644 index 000000000..5a43f6094 Binary files /dev/null and b/public/img/blog-avatar.jpg differ diff --git a/public/img/blog-avatar2.jpg b/public/img/blog-avatar2.jpg new file mode 100644 index 000000000..4319e8a32 Binary files /dev/null and b/public/img/blog-avatar2.jpg differ diff --git a/public/img/blog-medium.jpg b/public/img/blog-medium.jpg new file mode 100644 index 000000000..3736e1da3 Binary files /dev/null and b/public/img/blog-medium.jpg differ diff --git a/public/img/blog-recent-2.jpg b/public/img/blog-recent-2.jpg new file mode 100644 index 000000000..f3a3d19b4 Binary files /dev/null and b/public/img/blog-recent-2.jpg differ diff --git a/public/img/blog-recent-3.jpg b/public/img/blog-recent-3.jpg new file mode 100644 index 000000000..ad35c1211 Binary files /dev/null and b/public/img/blog-recent-3.jpg differ diff --git a/public/img/blog-recent.jpg b/public/img/blog-recent.jpg new file mode 100644 index 000000000..fd7aae6ca Binary files /dev/null and b/public/img/blog-recent.jpg differ diff --git a/public/img/blog.jpg b/public/img/blog.jpg new file mode 100644 index 000000000..541c867fd Binary files /dev/null and b/public/img/blog.jpg differ diff --git a/public/img/blog2.jpg b/public/img/blog2.jpg new file mode 100644 index 000000000..3910ba743 Binary files /dev/null and b/public/img/blog2.jpg differ diff --git a/public/img/cdg5.png b/public/img/cdg5.png new file mode 100644 index 000000000..16f59d744 Binary files /dev/null and b/public/img/cdg5.png differ diff --git a/public/img/cdg9.png b/public/img/cdg9.png new file mode 100644 index 000000000..9302d6e76 Binary files /dev/null and b/public/img/cdg9.png differ diff --git a/public/img/chicago.png b/public/img/chicago.png new file mode 100644 index 000000000..7bea86165 Binary files /dev/null and b/public/img/chicago.png differ diff --git a/public/img/cpg3.png b/public/img/cpg3.png new file mode 100644 index 000000000..e69a42c82 Binary files /dev/null and b/public/img/cpg3.png differ diff --git a/public/img/detailbig1.jpg b/public/img/detailbig1.jpg new file mode 100644 index 000000000..b52370cd5 Binary files /dev/null and b/public/img/detailbig1.jpg differ diff --git a/public/img/detailbig2.jpg b/public/img/detailbig2.jpg new file mode 100644 index 000000000..c519e8b84 Binary files /dev/null and b/public/img/detailbig2.jpg differ diff --git a/public/img/detailbig3.jpg b/public/img/detailbig3.jpg new file mode 100644 index 000000000..2ea58711d Binary files /dev/null and b/public/img/detailbig3.jpg differ diff --git a/public/img/detailsquare.jpg b/public/img/detailsquare.jpg new file mode 100644 index 000000000..b2b3a459f Binary files /dev/null and b/public/img/detailsquare.jpg differ diff --git a/public/img/detailsquare2.jpg b/public/img/detailsquare2.jpg new file mode 100644 index 000000000..d061be12d Binary files /dev/null and b/public/img/detailsquare2.jpg differ diff --git a/public/img/detailsquare3.jpg b/public/img/detailsquare3.jpg new file mode 100644 index 000000000..8f46a3980 Binary files /dev/null and b/public/img/detailsquare3.jpg differ diff --git a/public/img/favicon-old.ico b/public/img/favicon-old.ico new file mode 100644 index 000000000..01970e8f3 Binary files /dev/null and b/public/img/favicon-old.ico differ diff --git a/public/img/favicon.ico b/public/img/favicon.ico new file mode 100644 index 000000000..b162d6770 Binary files /dev/null and b/public/img/favicon.ico differ diff --git a/public/img/fixed-background-1.jpg b/public/img/fixed-background-1.jpg new file mode 100644 index 000000000..51a018de5 Binary files /dev/null and b/public/img/fixed-background-1.jpg differ diff --git a/public/img/fixed-background-2.jpg b/public/img/fixed-background-2.jpg new file mode 100644 index 000000000..34351c855 Binary files /dev/null and b/public/img/fixed-background-2.jpg differ diff --git a/public/img/grabbing.png b/public/img/grabbing.png new file mode 100644 index 000000000..252f81b2a Binary files /dev/null and b/public/img/grabbing.png differ diff --git a/public/img/home-try.jpg b/public/img/home-try.jpg new file mode 100644 index 000000000..df7666f31 Binary files /dev/null and b/public/img/home-try.jpg differ diff --git a/public/img/home.jpg b/public/img/home.jpg new file mode 100644 index 000000000..e236a42fd Binary files /dev/null and b/public/img/home.jpg differ diff --git a/public/img/homepage-slider.jpg b/public/img/homepage-slider.jpg new file mode 100644 index 000000000..696a47f4e Binary files /dev/null and b/public/img/homepage-slider.jpg differ diff --git a/public/img/logo-small.png b/public/img/logo-small.png new file mode 100644 index 000000000..c6aaa97cf Binary files /dev/null and b/public/img/logo-small.png differ diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100644 index 000000000..75508b815 Binary files /dev/null and b/public/img/logo.png differ diff --git a/public/img/main-slider1.jpg b/public/img/main-slider1.jpg new file mode 100644 index 000000000..192dc38b9 Binary files /dev/null and b/public/img/main-slider1.jpg differ diff --git a/public/img/main-slider2.jpg b/public/img/main-slider2.jpg new file mode 100644 index 000000000..7b96f50ba Binary files /dev/null and b/public/img/main-slider2.jpg differ diff --git a/public/img/main-slider3.jpg b/public/img/main-slider3.jpg new file mode 100644 index 000000000..4e91db7f5 Binary files /dev/null and b/public/img/main-slider3.jpg differ diff --git a/public/img/main-slider4.jpg b/public/img/main-slider4.jpg new file mode 100644 index 000000000..b211a15a2 Binary files /dev/null and b/public/img/main-slider4.jpg differ diff --git a/public/img/marker.png b/public/img/marker.png new file mode 100644 index 000000000..29cba8838 Binary files /dev/null and b/public/img/marker.png differ diff --git a/public/img/men.jpg b/public/img/men.jpg new file mode 100644 index 000000000..94b117c52 Binary files /dev/null and b/public/img/men.jpg differ diff --git a/public/img/mp.png b/public/img/mp.png new file mode 100644 index 000000000..841a28f1f Binary files /dev/null and b/public/img/mp.png differ diff --git a/public/img/page-1.jpg b/public/img/page-1.jpg new file mode 100644 index 000000000..301ab7cf6 Binary files /dev/null and b/public/img/page-1.jpg differ diff --git a/public/img/page-2.jpg b/public/img/page-2.jpg new file mode 100644 index 000000000..a054be241 Binary files /dev/null and b/public/img/page-2.jpg differ diff --git a/public/img/page-3.jpg b/public/img/page-3.jpg new file mode 100644 index 000000000..e3323b45e Binary files /dev/null and b/public/img/page-3.jpg differ diff --git a/public/img/payment.png b/public/img/payment.png new file mode 100644 index 000000000..3e24158dc Binary files /dev/null and b/public/img/payment.png differ diff --git a/public/img/photogrid.jpg b/public/img/photogrid.jpg new file mode 100644 index 000000000..137e11149 Binary files /dev/null and b/public/img/photogrid.jpg differ diff --git a/public/img/placeholder.png b/public/img/placeholder.png new file mode 100644 index 000000000..83228e5fe Binary files /dev/null and b/public/img/placeholder.png differ diff --git a/public/img/portfolio-1.jpg b/public/img/portfolio-1.jpg new file mode 100644 index 000000000..7f9a01718 Binary files /dev/null and b/public/img/portfolio-1.jpg differ diff --git a/public/img/portfolio-2.jpg b/public/img/portfolio-2.jpg new file mode 100644 index 000000000..54edd8756 Binary files /dev/null and b/public/img/portfolio-2.jpg differ diff --git a/public/img/portfolio-3.jpg b/public/img/portfolio-3.jpg new file mode 100644 index 000000000..58f666ebe Binary files /dev/null and b/public/img/portfolio-3.jpg differ diff --git a/public/img/portfolio-4.jpg b/public/img/portfolio-4.jpg new file mode 100644 index 000000000..2dbc99447 Binary files /dev/null and b/public/img/portfolio-4.jpg differ diff --git a/public/img/portfolio-5.jpg b/public/img/portfolio-5.jpg new file mode 100644 index 000000000..147fc43f3 Binary files /dev/null and b/public/img/portfolio-5.jpg differ diff --git a/public/img/portfolio-6.jpg b/public/img/portfolio-6.jpg new file mode 100644 index 000000000..54677b567 Binary files /dev/null and b/public/img/portfolio-6.jpg differ diff --git a/public/img/portfolio-7.jpg b/public/img/portfolio-7.jpg new file mode 100644 index 000000000..e7c1c6f0b Binary files /dev/null and b/public/img/portfolio-7.jpg differ diff --git a/public/img/portfolio-8.jpg b/public/img/portfolio-8.jpg new file mode 100644 index 000000000..9ec275d99 Binary files /dev/null and b/public/img/portfolio-8.jpg differ diff --git a/public/img/portfolio-9.jpg b/public/img/portfolio-9.jpg new file mode 100644 index 000000000..03ebd86fd Binary files /dev/null and b/public/img/portfolio-9.jpg differ diff --git a/public/img/product1.jpg b/public/img/product1.jpg new file mode 100644 index 000000000..0fd5db78e Binary files /dev/null and b/public/img/product1.jpg differ diff --git a/public/img/product2.jpg b/public/img/product2.jpg new file mode 100644 index 000000000..0a47585d7 Binary files /dev/null and b/public/img/product2.jpg differ diff --git a/public/img/product3.jpg b/public/img/product3.jpg new file mode 100644 index 000000000..c3d22f3e7 Binary files /dev/null and b/public/img/product3.jpg differ diff --git a/public/img/product4.jpg b/public/img/product4.jpg new file mode 100644 index 000000000..09b15fb15 Binary files /dev/null and b/public/img/product4.jpg differ diff --git a/public/img/skills-small.jpg b/public/img/skills-small.jpg new file mode 100644 index 000000000..ce0c08bcf Binary files /dev/null and b/public/img/skills-small.jpg differ diff --git a/public/img/slide1.jpg b/public/img/slide1.jpg new file mode 100644 index 000000000..cbb30ee1e Binary files /dev/null and b/public/img/slide1.jpg differ diff --git a/public/img/slide2.jpg b/public/img/slide2.jpg new file mode 100644 index 000000000..0d5ef400f Binary files /dev/null and b/public/img/slide2.jpg differ diff --git a/public/img/slide3.jpg b/public/img/slide3.jpg new file mode 100644 index 000000000..da9b6da9b Binary files /dev/null and b/public/img/slide3.jpg differ diff --git a/public/img/slide4.jpg b/public/img/slide4.jpg new file mode 100644 index 000000000..83889ce26 Binary files /dev/null and b/public/img/slide4.jpg differ diff --git a/public/img/slide5.jpg b/public/img/slide5.jpg new file mode 100644 index 000000000..d4749b723 Binary files /dev/null and b/public/img/slide5.jpg differ diff --git a/public/img/slide6.jpg b/public/img/slide6.jpg new file mode 100644 index 000000000..48f72bc29 Binary files /dev/null and b/public/img/slide6.jpg differ diff --git a/public/img/soni.jpg b/public/img/soni.jpg new file mode 100644 index 000000000..e6d6eefe9 Binary files /dev/null and b/public/img/soni.jpg differ diff --git a/public/img/texture-bw.png b/public/img/texture-bw.png new file mode 100644 index 000000000..4215594c6 Binary files /dev/null and b/public/img/texture-bw.png differ diff --git a/public/img/texture-green.png b/public/img/texture-green.png new file mode 100644 index 000000000..21b923198 Binary files /dev/null and b/public/img/texture-green.png differ diff --git a/public/img/texture-turquoise.png b/public/img/texture-turquoise.png new file mode 100644 index 000000000..8da535190 Binary files /dev/null and b/public/img/texture-turquoise.png differ diff --git a/public/img/texture-violet.png b/public/img/texture-violet.png new file mode 100644 index 000000000..81a4e1add Binary files /dev/null and b/public/img/texture-violet.png differ diff --git a/public/img/women.jpg b/public/img/women.jpg new file mode 100644 index 000000000..7e0e6dbcf Binary files /dev/null and b/public/img/women.jpg differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 000000000..79d952e74 --- /dev/null +++ b/public/index.html @@ -0,0 +1,1039 @@ + + + + + + + + + +Canadian Data Guy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + +
+
+
+
+
+

What People Say About Me?

+
+ +

+ +

+ + + + + + + +
+ +
+
+
+ + + + + + + + + + + + +
+
+ +
+
+

From our blog

+
+ +

+ +

+ + + +
+ + +
+
+
+
+ + + +
+
+
+

+ Read more +

+
+
+ +
+

Solving Delta Table Concurrency Issues

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + on September 30, 2023 + +

+ +

Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

+

+ Continue reading +

+ +
+
+ + +
+ +
+
+
+
+ + + +
+
+
+

+ Read more +

+
+
+ +
+

Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + on September 15, 2023 + +

+ +

Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar.

+

+ Continue reading +

+ +
+
+ + +
+ +
+
+
+
+ + + +
+
+
+

+ Read more +

+
+
+ +
+

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + on September 12, 2023 + +

+ +

Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial?

+

+ Continue reading +

+ +
+
+ + +
+ +
+
+
+
+ + + +
+
+
+

+ Read more +

+
+
+ +
+

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + on June 6, 2023 + +

+ +

Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

+

+ Continue reading +

+ +
+
+ + +
+ + +
+ + + + +
+
+ +
+ + + + + + + + + + +
+
+ + +
+

About us

+ +

We offer expertise and consulting in data engineering, analytics and cloud computing.

+ + + +
+ + + +
+ + + +
+ + + +
+ +

Contact

+ +

Canadian Data Guy Corp. +
Calgary, Canada +

+ + + Go to contact page + + + +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/index.xml b/public/index.xml new file mode 100644 index 000000000..4e0bb8cec --- /dev/null +++ b/public/index.xml @@ -0,0 +1,212 @@ + + + + Canadian Data Guy + https://canadiandataguy.com/ + Recent content on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You&rsquo;re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. + + + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Tue, 12 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + A Productive Life: How to Parallelize Code Execution in Python + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can. + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. + + + How to get the Job ID and Run ID for a Databricks Job + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f&quot;&quot;&quot; job_id: {dbutils. + + + How to prepare yourself to be better at Data Interviews? + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python &amp; Big Data fundamentals. Here is how to prepare for each of them. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + 1 on 1 Interview Coaching + https://canadiandataguy.com/blog/1-on-1-coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/1-on-1-coaching/ + Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks &amp; Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + + About Me + https://canadiandataguy.com/about-me/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/about-me/ + I am an end-to-end Full Stack Data Developer who can set up infrastructures, design the architecture, build data pipelines, and productionalize with DevOps. I have spent six years in AWS &amp; Amazon as a Data Architect/Engineer and have a proven record of taking multi-petabyte workloads to production while ensuring minimum operational burden. I also have hands-on experience in Streaming and Batch Analytics, including real-time applications. Having worked remotely for around 6+ years, I’ve learned to avoid the pitfalls of distributed team communication. + + + Contact + https://canadiandataguy.com/contact/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/contact/ + Do you need Data Expertise? Does your company have a project that requires data engineering, data architecture or streaming expertise? Don&rsquo;t hesitate to email us. We are happy to answer any questions or requests you may have. We can help with one-off projects or ongoing efforts to advance the shift to a more data-driven culture. Empower leaders and teams better to use data as a tool in the decision-making process. + + + Databricks Customer Stories + https://canadiandataguy.com/databricks-customer-success-stories/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/databricks-customer-success-stories/ + I have worked with multiple customers on their data journey. Here are some of the public stories that I have co-authored with them. +Audantic To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. + + + Youtube + https://canadiandataguy.com/blog/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/youtube/ + + + + diff --git a/public/js/front.js b/public/js/front.js new file mode 100644 index 000000000..b520584f9 --- /dev/null +++ b/public/js/front.js @@ -0,0 +1,386 @@ +/* global $this: true */ +/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "animationsSlider" }] */ + +if ($.cookie('themeCSSpath')) { + $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath')) +} +if ($.cookie('themeLayout')) { + $('body').addClass($.cookie('themeLayout')) +} + +$(function () { + sliderHomepage() + sliders() + fullScreenContainer() + productDetailGallery(4000) + menuSliding() + menuMouseOver() + productDetailSizes() + utils() + animations() + counters() + demo() + contactFormAjax() +}) + +// Ajax contact +function contactFormAjax () { + var form = $('.contact-form-ajax') + if (typeof form === 'undefined') return false + form.submit(function () { + $this = $(this) + $.post($(this).attr('action'), + $this.serialize(), + function () { + $this[0].reset() // clear form + + $('#contact-message') + .html('') + .fadeIn() + } + , 'json') + return false + }) +} + +/* for demo purpose only - can be deleted */ +function demo () { + if ($.cookie('themeCSSpath')) { + $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath')) + } + + $('#colour').change(function () { + if ($(this).val() !== '') { + var themeCSSpath = 'css/style.' + $(this).val() + '.css' + + $('link#theme-stylesheet').attr('href', themeCSSpath) + + $.cookie('themeCSSpath', themeCSSpath, {expires: 365, path: '/'}) + } + + return false + }) + + $('#layout').change(function () { + if ($(this).val() !== '') { + var themeLayout = $(this).val() + + $('body').removeClass('wide') + $('body').removeClass('boxed') + + $('body').addClass(themeLayout) + + $.cookie('themeLayout', themeLayout, {expires: 365, path: '/'}) + } + + return false + }) +} + +/* slider homepage */ +function sliderHomepage () { + if ($('#slider').length) { + // var owl = $('#slider') + + $('#slider').owlCarousel({ + autoPlay: 3000, + items: 4, + itemsDesktopSmall: [900, 3], + itemsTablet: [600, 3], + itemsMobile: [500, 2] + }) + } +} + +/* sliders */ +function sliders () { + if ($('.owl-carousel').length) { + $('.customers').owlCarousel({ + items: ($('.customers').attr('data-items') || 6), + slideSpeed: ($('.customers').attr('data-slide-speed') || 2000), + paginationSpeed: ($('.customers').attr('data-pagination-speed') || 1000), + autoPlay: $('.customers').attr('data-autoplay') === 'true', + itemsDesktopSmall: [990, 4], + itemsTablet: [768, 2], + itemsMobile: [480, 1] + }) + + $('.testimonials').owlCarousel({ + items: ($('.testimonials').attr('data-items') || 4), + slideSpeed: ($('.testimonials').attr('data-slide-speed') || 2000), + paginationSpeed: ($('.testimonials').attr('data-pagination-speed') || 1000), + autoPlay: $('.testimonials').attr('data-autoplay') === 'true', + itemsDesktopSmall: [990, 3], + itemsTablet: [768, 2], + itemsMobile: [480, 1] + }) + + $('.homepage').owlCarousel({ + navigation: false, // Show next and prev buttons + navigationText: ['', ''], + slideSpeed: ($('.homepage').attr('data-slide-speed') || 2000), + paginationSpeed: ($('.homepage').attr('data-pagination-speed') || 1000), + autoPlay: ($('.homepage').attr('data-autoplay') || 'true') === 'true', + stopOnHover: true, + singleItem: true, + lazyLoad: false, + addClassActive: true, + afterInit: function () { + // animationsSlider() + }, + afterMove: function () { + // animationsSlider() + } + }) + } +} + +/* menu sliding */ +function menuSliding () { + $('.dropdown').on('show.bs.dropdown', function () { + if ($(window).width() > 750) { + $(this).find('.dropdown-menu').first().stop(true, true).slideDown() + } else { + $(this).find('.dropdown-menu').first().stop(true, true).show() + } + }) + + $('.dropdown').on('hide.bs.dropdown', function () { + if ($(window).width() > 750) { + $(this).find('.dropdown-menu').first().stop(true, true).slideUp() + } else { + $(this).find('.dropdown-menu').first().stop(true, true).hide() + } + }) +} + +function menuMouseOver () { + $('.mouseover .dropdown').hover(function () { + $('.dropdown-toggle', this).trigger('click') + }) +} + +/* animations */ +function animations () { + var delayTime = 0 + $('[data-animate]').css({opacity: '0'}) + $('[data-animate]').waypoint(function () { + delayTime += 150 + $(this).delay(delayTime).queue(function (next) { + $(this).toggleClass('animated') + $(this).toggleClass($(this).data('animate')) + delayTime = 0 + next() + // $(this).removeClass('animated') + // $(this).toggleClass($(this).data('animate')) + }) + }, { + offset: '90%', + triggerOnce: true + }) + + $('[data-animate-hover]').hover(function () { + $(this).css({opacity: 1}) + $(this).addClass('animated') + $(this).removeClass($(this).data('animate')) + $(this).addClass($(this).data('animate-hover')) + }, function () { + $(this).removeClass('animated') + $(this).removeClass($(this).data('animate-hover')) + }) +} + +function animationsSlider () { + var delayTimeSlider = 400 + + $('.owl-item:not(.active) [data-animate-always]').each(function () { + $(this).removeClass('animated') + $(this).removeClass($(this).data('animate-always')) + $(this).stop(true, true, true).css({opacity: 0}) + }) + + $('.owl-item.active [data-animate-always]').each(function () { + delayTimeSlider += 500 + + $(this).delay(delayTimeSlider).queue(function () { + $(this).addClass('animated') + $(this).addClass($(this).data('animate-always')) + + console.log($(this).data('animate-always')) + }) + }) +} + +/* counters */ +function counters () { + $('.counter').counterUp({ + delay: 10, + time: 1000 + }) +} + +/* picture zoom */ +function pictureZoom () { + $('.product .image, .post .image, .photostream div').each(function () { + var imgHeight = $(this).find('img').height() + if (imgHeight) { + $(this).height(imgHeight) + } + }) +} + +/* full screen intro */ +function fullScreenContainer () { + var screenWidth = $(window).width() + 'px' + var screenHeight = '500px' + + if ($(window).height() > 500) { + screenHeight = $(window).height() + 'px' + } + + $('#intro, #intro .item').css({ + width: screenWidth, + height: screenHeight + }) +} + +function utils () { + /* tooltips */ + $('[data-toggle="tooltip"]').tooltip() + + /* click on the box activates the radio */ + $('#checkout').on('click', '.box.shipping-method, .box.payment-method', function () { + var radio = $(this).find(':radio') + radio.prop('checked', true) + }) + + /* click on the box activates the link in it */ + $('.box.clickable').on('click', function () { + window.location = $(this).find('a').attr('href') + }) + + /* external links in new window */ + $('.external').on('click', function (e) { + e.preventDefault() + window.open($(this).attr('href')) + }) + + /* animated scrolling */ + $('.scroll-to, .scroll-to-top').click(function (event) { + var fullUrl = this.href + var parts = fullUrl.split('#') + + if (parts.length > 1) { + scrollTo(fullUrl) + event.preventDefault() + } + }) + + function scrollTo (fullUrl) { + var parts = fullUrl.split('#') + var trgt = parts[1] + var targetOffset = $('#' + trgt).offset() + var targetTop = targetOffset.top - 100 + + if (targetTop < 0) { + targetTop = 0 + } + + $('html, body').animate({ + scrollTop: targetTop + }, 1000) + } +} + +/* product detail gallery */ +function productDetailGallery (confDetailSwitch) { + $('.thumb:first').addClass('active') + var timer = setInterval(autoSwitch, confDetailSwitch) + + $('.thumb').click(function (e) { + switchImage($(this)) + clearInterval(timer) + timer = setInterval(autoSwitch, confDetailSwitch) + e.preventDefault() + }) + + $('#mainImage').hover(function () { + clearInterval(timer) + }, function () { + timer = setInterval(autoSwitch, confDetailSwitch) + }) + + function autoSwitch () { + var nextThumb = $('.thumb.active').closest('div').next('div').find('.thumb') + if (nextThumb.length === 0) { + nextThumb = $('.thumb:first') + } + switchImage(nextThumb) + } + + function switchImage (thumb) { + $('.thumb').removeClass('active') + var bigUrl = thumb.attr('href') + thumb.addClass('active') + $('#mainImage img').attr('src', bigUrl) + } +} + +/* product detail sizes */ +function productDetailSizes () { + $('.sizes a').click(function (e) { + e.preventDefault() + $('.sizes a').removeClass('active') + $('.size-input').prop('checked', false) + $(this).addClass('active') + $(this).next('input').prop('checked', true) + }) +} + +$.fn.alignElementsSameHeight = function () { + $('.same-height-row').each(function () { + var maxHeight = 0 + var children = $(this).find('.same-height') + children.height('auto') + + if ($(window).width() > 768) { + children.each(function () { + if ($(this).innerHeight() > maxHeight) { + maxHeight = $(this).innerHeight() + } + }) + children.innerHeight(maxHeight) + } + + maxHeight = 0 + children = $(this).find('.same-height-always') + children.height('auto') + children.each(function () { + if ($(this).height() > maxHeight) { + maxHeight = $(this).innerHeight() + } + }) + children.innerHeight(maxHeight) + }) +} + +var windowWidth +$(function () { + windowWidth = $(window).width() + + $(this).alignElementsSameHeight() + pictureZoom() +}) + +$(window).resize(function () { + var newWindowWidth = $(window).width() + + if (windowWidth !== newWindowWidth) { + setTimeout(function () { + $(this).alignElementsSameHeight() + fullScreenContainer() + pictureZoom() + }, 205) + windowWidth = newWindowWidth + } +}) diff --git a/public/js/gmaps.init.js b/public/js/gmaps.init.js new file mode 100644 index 000000000..2227dda92 --- /dev/null +++ b/public/js/gmaps.init.js @@ -0,0 +1,71 @@ +/* global GMaps: true */ + +$(document).ready(function () { + map() +}) + +function map () { + if ($('#map').length) { + var lat = $('#gmap-lat').val() + var lng = $('#gmap-lng').val() + var direction = $('#gmap-dir').val() + var image = $('#gmap-marker').val() + + var styles = + [ + { + 'featureType': 'landscape', 'stylers': [{'saturation': -100}, {'lightness': 65}, {'visibility': 'on'}] + }, { + 'featureType': 'poi', 'stylers': [{'saturation': -100}, {'lightness': 51}, {'visibility': 'simplified'}] + }, { + 'featureType': 'road.highway', 'stylers': [{'saturation': -100}, {'visibility': 'simplified'}] + }, { + 'featureType': 'road.arterial', 'stylers': [{'saturation': -100}, {'lightness': 30}, {'visibility': 'on'}] + }, { + 'featureType': 'road.local', 'stylers': [{'saturation': -100}, {'lightness': 40}, {'visibility': 'on'}] + }, { + 'featureType': 'transit', 'stylers': [{'saturation': -100}, {'visibility': 'simplified'}] + }, { + 'featureType': 'administrative.province', 'stylers': [{'visibility': 'off'}] + }, { + 'featureType': 'water', 'elementType': 'labels', 'stylers': [{'visibility': 'on'}, {'lightness': -25}, {'saturation': -100}] + }, { + 'featureType': 'water', 'elementType': 'geometry', 'stylers': [{'hue': '#ffff00'}, {'lightness': -25}, {'saturation': -97}] + } + ] + + var map = new GMaps({ + el: '#map', + lat: lat, + lng: lng, + zoomControl: true, + zoomControlOpt: { + style: 'SMALL', + position: 'TOP_LEFT' + }, + panControl: false, + streetViewControl: false, + mapTypeControl: false, + overviewMapControl: false, + scrollwheel: false, + draggable: false, + styles: styles + }) + + map.addMarker({ + lat: lat, + lng: lng, + icon: image, + click: function (e) { + // when we get an address with spaces ... + var url = 'https://maps.google.com?daddr=' + direction.split('match').join('replace') + window.open(url, '_blank') + }, + title: direction + /* , + infoWindow: { + content: '

HTML Content

' + } */ + }) + } +} diff --git a/public/js/hpneo.gmaps.js b/public/js/hpneo.gmaps.js new file mode 100644 index 000000000..66b4959de --- /dev/null +++ b/public/js/hpneo.gmaps.js @@ -0,0 +1,2132 @@ +(function(root, factory) { + if(typeof exports === 'object') { + module.exports = factory(); + } + else if(typeof define === 'function' && define.amd) { + define('GMaps', [], factory); + } + + root.GMaps = factory(); + +}(this, function() { + +/*! + * GMaps.js v0.4.15 + * http://hpneo.github.com/gmaps/ + * + * Copyright 2014, Gustavo Leon + * Released under the MIT License. + */ + +if (!(typeof window.google === 'object' && window.google.maps)) { + throw 'Google Maps API is required. Please register the following JavaScript library http://maps.google.com/maps/api/js?sensor=true.' +} + +var extend_object = function(obj, new_obj) { + var name; + + if (obj === new_obj) { + return obj; + } + + for (name in new_obj) { + obj[name] = new_obj[name]; + } + + return obj; +}; + +var replace_object = function(obj, replace) { + var name; + + if (obj === replace) { + return obj; + } + + for (name in replace) { + if (obj[name] != undefined) { + obj[name] = replace[name]; + } + } + + return obj; +}; + +var array_map = function(array, callback) { + var original_callback_params = Array.prototype.slice.call(arguments, 2), + array_return = [], + array_length = array.length, + i; + + if (Array.prototype.map && array.map === Array.prototype.map) { + array_return = Array.prototype.map.call(array, function(item) { + callback_params = original_callback_params; + callback_params.splice(0, 0, item); + + return callback.apply(this, callback_params); + }); + } + else { + for (i = 0; i < array_length; i++) { + callback_params = original_callback_params; + callback_params.splice(0, 0, array[i]); + array_return.push(callback.apply(this, callback_params)); + } + } + + return array_return; +}; + +var array_flat = function(array) { + var new_array = [], + i; + + for (i = 0; i < array.length; i++) { + new_array = new_array.concat(array[i]); + } + + return new_array; +}; + +var coordsToLatLngs = function(coords, useGeoJSON) { + var first_coord = coords[0], + second_coord = coords[1]; + + if (useGeoJSON) { + first_coord = coords[1]; + second_coord = coords[0]; + } + + return new google.maps.LatLng(first_coord, second_coord); +}; + +var arrayToLatLng = function(coords, useGeoJSON) { + var i; + + for (i = 0; i < coords.length; i++) { + if (!(coords[i] instanceof google.maps.LatLng)) { + if (coords[i].length > 0 && typeof(coords[i][0]) == "object") { + coords[i] = arrayToLatLng(coords[i], useGeoJSON); + } + else { + coords[i] = coordsToLatLngs(coords[i], useGeoJSON); + } + } + } + + return coords; +}; + +var getElementById = function(id, context) { + var element, + id = id.replace('#', ''); + + if ('jQuery' in this && context) { + element = $("#" + id, context)[0]; + } else { + element = document.getElementById(id); + }; + + return element; +}; + +var findAbsolutePosition = function(obj) { + var curleft = 0, + curtop = 0; + + if (obj.offsetParent) { + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } while (obj = obj.offsetParent); + } + + return [curleft, curtop]; +}; + +var GMaps = (function(global) { + "use strict"; + + var doc = document; + + var GMaps = function(options) { + if (!this) return new GMaps(options); + + options.zoom = options.zoom || 15; + options.mapType = options.mapType || 'roadmap'; + + var self = this, + i, + events_that_hide_context_menu = ['bounds_changed', 'center_changed', 'click', 'dblclick', 'drag', 'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed', 'resize', 'tilesloaded', 'zoom_changed'], + events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'], + options_to_be_deleted = ['el', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle'], + container_id = options.el || options.div, + markerClustererFunction = options.markerClusterer, + mapType = google.maps.MapTypeId[options.mapType.toUpperCase()], + map_center = new google.maps.LatLng(options.lat, options.lng), + zoomControl = options.zoomControl || true, + zoomControlOpt = options.zoomControlOpt || { + style: 'DEFAULT', + position: 'TOP_LEFT' + }, + zoomControlStyle = zoomControlOpt.style || 'DEFAULT', + zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT', + panControl = options.panControl || true, + mapTypeControl = options.mapTypeControl || true, + scaleControl = options.scaleControl || true, + streetViewControl = options.streetViewControl || true, + overviewMapControl = overviewMapControl || true, + map_options = {}, + map_base_options = { + zoom: this.zoom, + center: map_center, + mapTypeId: mapType + }, + map_controls_options = { + panControl: panControl, + zoomControl: zoomControl, + zoomControlOptions: { + style: google.maps.ZoomControlStyle[zoomControlStyle], + position: google.maps.ControlPosition[zoomControlPosition] + }, + mapTypeControl: mapTypeControl, + scaleControl: scaleControl, + streetViewControl: streetViewControl, + overviewMapControl: overviewMapControl + }; + + if (typeof(options.el) === 'string' || typeof(options.div) === 'string') { + this.el = getElementById(container_id, options.context); + } else { + this.el = container_id; + } + + if (typeof(this.el) === 'undefined' || this.el === null) { + throw 'No element defined.'; + } + + window.context_menu = window.context_menu || {}; + window.context_menu[self.el.id] = {}; + + this.controls = []; + this.overlays = []; + this.layers = []; // array with kml/georss and fusiontables layers, can be as many + this.singleLayers = {}; // object with the other layers, only one per layer + this.markers = []; + this.polylines = []; + this.routes = []; + this.polygons = []; + this.infoWindow = null; + this.overlay_el = null; + this.zoom = options.zoom; + this.registered_events = {}; + + this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth; + this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight; + + google.maps.visualRefresh = options.enableNewStyle; + + for (i = 0; i < options_to_be_deleted.length; i++) { + delete options[options_to_be_deleted[i]]; + } + + if(options.disableDefaultUI != true) { + map_base_options = extend_object(map_base_options, map_controls_options); + } + + map_options = extend_object(map_base_options, options); + + for (i = 0; i < events_that_hide_context_menu.length; i++) { + delete map_options[events_that_hide_context_menu[i]]; + } + + for (i = 0; i < events_that_doesnt_hide_context_menu.length; i++) { + delete map_options[events_that_doesnt_hide_context_menu[i]]; + } + + this.map = new google.maps.Map(this.el, map_options); + + if (markerClustererFunction) { + this.markerClusterer = markerClustererFunction.apply(this, [this.map]); + } + + var buildContextMenuHTML = function(control, e) { + var html = '', + options = window.context_menu[self.el.id][control]; + + for (var i in options){ + if (options.hasOwnProperty(i)) { + var option = options[i]; + + html += '
  • ' + option.title + '
  • '; + } + } + + if (!getElementById('gmaps_context_menu')) return; + + var context_menu_element = getElementById('gmaps_context_menu'); + + context_menu_element.innerHTML = html; + + var context_menu_items = context_menu_element.getElementsByTagName('a'), + context_menu_items_count = context_menu_items.length, + i; + + for (i = 0; i < context_menu_items_count; i++) { + var context_menu_item = context_menu_items[i]; + + var assign_menu_item_action = function(ev){ + ev.preventDefault(); + + options[this.id.replace(control + '_', '')].action.apply(self, [e]); + self.hideContextMenu(); + }; + + google.maps.event.clearListeners(context_menu_item, 'click'); + google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false); + } + + var position = findAbsolutePosition.apply(this, [self.el]), + left = position[0] + e.pixel.x - 15, + top = position[1] + e.pixel.y- 15; + + context_menu_element.style.left = left + "px"; + context_menu_element.style.top = top + "px"; + + context_menu_element.style.display = 'block'; + }; + + this.buildContextMenu = function(control, e) { + if (control === 'marker') { + e.pixel = {}; + + var overlay = new google.maps.OverlayView(); + overlay.setMap(self.map); + + overlay.draw = function() { + var projection = overlay.getProjection(), + position = e.marker.getPosition(); + + e.pixel = projection.fromLatLngToContainerPixel(position); + + buildContextMenuHTML(control, e); + }; + } + else { + buildContextMenuHTML(control, e); + } + }; + + this.setContextMenu = function(options) { + window.context_menu[self.el.id][options.control] = {}; + + var i, + ul = doc.createElement('ul'); + + for (i in options.options) { + if (options.options.hasOwnProperty(i)) { + var option = options.options[i]; + + window.context_menu[self.el.id][options.control][option.name] = { + title: option.title, + action: option.action + }; + } + } + + ul.id = 'gmaps_context_menu'; + ul.style.display = 'none'; + ul.style.position = 'absolute'; + ul.style.minWidth = '100px'; + ul.style.background = 'white'; + ul.style.listStyle = 'none'; + ul.style.padding = '8px'; + ul.style.boxShadow = '2px 2px 6px #ccc'; + + doc.body.appendChild(ul); + + var context_menu_element = getElementById('gmaps_context_menu') + + google.maps.event.addDomListener(context_menu_element, 'mouseout', function(ev) { + if (!ev.relatedTarget || !this.contains(ev.relatedTarget)) { + window.setTimeout(function(){ + context_menu_element.style.display = 'none'; + }, 400); + } + }, false); + }; + + this.hideContextMenu = function() { + var context_menu_element = getElementById('gmaps_context_menu'); + + if (context_menu_element) { + context_menu_element.style.display = 'none'; + } + }; + + var setupListener = function(object, name) { + google.maps.event.addListener(object, name, function(e){ + if (e == undefined) { + e = this; + } + + options[name].apply(this, [e]); + + self.hideContextMenu(); + }); + }; + + //google.maps.event.addListener(this.map, 'idle', this.hideContextMenu); + google.maps.event.addListener(this.map, 'zoom_changed', this.hideContextMenu); + + for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) { + var name = events_that_hide_context_menu[ev]; + + if (name in options) { + setupListener(this.map, name); + } + } + + for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) { + var name = events_that_doesnt_hide_context_menu[ev]; + + if (name in options) { + setupListener(this.map, name); + } + } + + google.maps.event.addListener(this.map, 'rightclick', function(e) { + if (options.rightclick) { + options.rightclick.apply(this, [e]); + } + + if(window.context_menu[self.el.id]['map'] != undefined) { + self.buildContextMenu('map', e); + } + }); + + this.refresh = function() { + google.maps.event.trigger(this.map, 'resize'); + }; + + this.fitZoom = function() { + var latLngs = [], + markers_length = this.markers.length, + i; + + for (i = 0; i < markers_length; i++) { + if(typeof(this.markers[i].visible) === 'boolean' && this.markers[i].visible) { + latLngs.push(this.markers[i].getPosition()); + } + } + + this.fitLatLngBounds(latLngs); + }; + + this.fitLatLngBounds = function(latLngs) { + var total = latLngs.length; + var bounds = new google.maps.LatLngBounds(); + + for(var i=0; i < total; i++) { + bounds.extend(latLngs[i]); + } + + this.map.fitBounds(bounds); + }; + + this.setCenter = function(lat, lng, callback) { + this.map.panTo(new google.maps.LatLng(lat, lng)); + + if (callback) { + callback(); + } + }; + + this.getElement = function() { + return this.el; + }; + + this.zoomIn = function(value) { + value = value || 1; + + this.zoom = this.map.getZoom() + value; + this.map.setZoom(this.zoom); + }; + + this.zoomOut = function(value) { + value = value || 1; + + this.zoom = this.map.getZoom() - value; + this.map.setZoom(this.zoom); + }; + + var native_methods = [], + method; + + for (method in this.map) { + if (typeof(this.map[method]) == 'function' && !this[method]) { + native_methods.push(method); + } + } + + for (i=0; i < native_methods.length; i++) { + (function(gmaps, scope, method_name) { + gmaps[method_name] = function(){ + return scope[method_name].apply(scope, arguments); + }; + })(this, this.map, native_methods[i]); + } + }; + + return GMaps; +})(this); + +GMaps.prototype.createControl = function(options) { + var control = document.createElement('div'); + + control.style.cursor = 'pointer'; + + if (options.disableDefaultStyles !== true) { + control.style.fontFamily = 'Roboto, Arial, sans-serif'; + control.style.fontSize = '11px'; + control.style.boxShadow = 'rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px'; + } + + for (var option in options.style) { + control.style[option] = options.style[option]; + } + + if (options.id) { + control.id = options.id; + } + + if (options.classes) { + control.className = options.classes; + } + + if (options.content) { + if (typeof options.content === 'string') { + control.innerHTML = options.content; + } + else if (options.content instanceof HTMLElement) { + control.appendChild(options.content); + } + } + + if (options.position) { + control.position = google.maps.ControlPosition[options.position.toUpperCase()]; + } + + for (var ev in options.events) { + (function(object, name) { + google.maps.event.addDomListener(object, name, function(){ + options.events[name].apply(this, [this]); + }); + })(control, ev); + } + + control.index = 1; + + return control; +}; + +GMaps.prototype.addControl = function(options) { + var control = this.createControl(options); + this.controls.push(control); + this.map.controls[control.position].push(control); + + return control; +}; + +GMaps.prototype.removeControl = function(control) { + var position = null; + + for (var i = 0; i < this.controls.length; i++) { + if (this.controls[i] == control) { + position = this.controls[i].position; + this.controls.splice(i, 1); + } + } + + if (position) { + for (i = 0; i < this.map.controls.length; i++) { + var controlsForPosition = this.map.controls[control.position] + if (controlsForPosition.getAt(i) == control) { + controlsForPosition.removeAt(i); + break; + } + } + } + + return control; +}; + +GMaps.prototype.createMarker = function(options) { + if (options.lat == undefined && options.lng == undefined && options.position == undefined) { + throw 'No latitude or longitude defined.'; + } + + var self = this, + details = options.details, + fences = options.fences, + outside = options.outside, + base_options = { + position: new google.maps.LatLng(options.lat, options.lng), + map: null + }, + marker_options = extend_object(base_options, options); + + delete marker_options.lat; + delete marker_options.lng; + delete marker_options.fences; + delete marker_options.outside; + + var marker = new google.maps.Marker(marker_options); + + marker.fences = fences; + + if (options.infoWindow) { + marker.infoWindow = new google.maps.InfoWindow(options.infoWindow); + + var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed']; + + for (var ev = 0; ev < info_window_events.length; ev++) { + (function(object, name) { + if (options.infoWindow[name]) { + google.maps.event.addListener(object, name, function(e){ + options.infoWindow[name].apply(this, [e]); + }); + } + })(marker.infoWindow, info_window_events[ev]); + } + } + + var marker_events = ['animation_changed', 'clickable_changed', 'cursor_changed', 'draggable_changed', 'flat_changed', 'icon_changed', 'position_changed', 'shadow_changed', 'shape_changed', 'title_changed', 'visible_changed', 'zindex_changed']; + + var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup']; + + for (var ev = 0; ev < marker_events.length; ev++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(){ + options[name].apply(this, [this]); + }); + } + })(marker, marker_events[ev]); + } + + for (var ev = 0; ev < marker_events_with_mouse.length; ev++) { + (function(map, object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(me){ + if(!me.pixel){ + me.pixel = map.getProjection().fromLatLngToPoint(me.latLng) + } + + options[name].apply(this, [me]); + }); + } + })(this.map, marker, marker_events_with_mouse[ev]); + } + + google.maps.event.addListener(marker, 'click', function() { + this.details = details; + + if (options.click) { + options.click.apply(this, [this]); + } + + if (marker.infoWindow) { + self.hideInfoWindows(); + marker.infoWindow.open(self.map, marker); + } + }); + + google.maps.event.addListener(marker, 'rightclick', function(e) { + e.marker = this; + + if (options.rightclick) { + options.rightclick.apply(this, [e]); + } + + if (window.context_menu[self.el.id]['marker'] != undefined) { + self.buildContextMenu('marker', e); + } + }); + + if (marker.fences) { + google.maps.event.addListener(marker, 'dragend', function() { + self.checkMarkerGeofence(marker, function(m, f) { + outside(m, f); + }); + }); + } + + return marker; +}; + +GMaps.prototype.addMarker = function(options) { + var marker; + if(options.hasOwnProperty('gm_accessors_')) { + // Native google.maps.Marker object + marker = options; + } + else { + if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) { + marker = this.createMarker(options); + } + else { + throw 'No latitude or longitude defined.'; + } + } + + marker.setMap(this.map); + + if(this.markerClusterer) { + this.markerClusterer.addMarker(marker); + } + + this.markers.push(marker); + + GMaps.fire('marker_added', marker, this); + + return marker; +}; + +GMaps.prototype.addMarkers = function(array) { + for (var i = 0, marker; marker=array[i]; i++) { + this.addMarker(marker); + } + + return this.markers; +}; + +GMaps.prototype.hideInfoWindows = function() { + for (var i = 0, marker; marker = this.markers[i]; i++){ + if (marker.infoWindow) { + marker.infoWindow.close(); + } + } +}; + +GMaps.prototype.removeMarker = function(marker) { + for (var i = 0; i < this.markers.length; i++) { + if (this.markers[i] === marker) { + this.markers[i].setMap(null); + this.markers.splice(i, 1); + + if(this.markerClusterer) { + this.markerClusterer.removeMarker(marker); + } + + GMaps.fire('marker_removed', marker, this); + + break; + } + } + + return marker; +}; + +GMaps.prototype.removeMarkers = function (collection) { + var new_markers = []; + + if (typeof collection == 'undefined') { + for (var i = 0; i < this.markers.length; i++) { + this.markers[i].setMap(null); + } + + this.markers = new_markers; + } + else { + for (var i = 0; i < collection.length; i++) { + if (this.markers.indexOf(collection[i]) > -1) { + this.markers[i].setMap(null); + } + } + + for (var i = 0; i < this.markers.length; i++) { + if (this.markers[i].getMap() != null) { + new_markers.push(this.markers[i]); + } + } + + this.markers = new_markers; + } +}; + +GMaps.prototype.drawOverlay = function(options) { + var overlay = new google.maps.OverlayView(), + auto_show = true; + + overlay.setMap(this.map); + + if (options.auto_show != null) { + auto_show = options.auto_show; + } + + overlay.onAdd = function() { + var el = document.createElement('div'); + + el.style.borderStyle = "none"; + el.style.borderWidth = "0px"; + el.style.position = "absolute"; + el.style.zIndex = 100; + el.innerHTML = options.content; + + overlay.el = el; + + if (!options.layer) { + options.layer = 'overlayLayer'; + } + + var panes = this.getPanes(), + overlayLayer = panes[options.layer], + stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown']; + + overlayLayer.appendChild(el); + + for (var ev = 0; ev < stop_overlay_events.length; ev++) { + (function(object, name) { + google.maps.event.addDomListener(object, name, function(e){ + if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) { + e.cancelBubble = true; + e.returnValue = false; + } + else { + e.stopPropagation(); + } + }); + })(el, stop_overlay_events[ev]); + } + + if (options.click) { + panes.overlayMouseTarget.appendChild(overlay.el); + google.maps.event.addDomListener(overlay.el, 'click', function() { + options.click.apply(overlay, [overlay]); + }); + } + + google.maps.event.trigger(this, 'ready'); + }; + + overlay.draw = function() { + var projection = this.getProjection(), + pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng)); + + options.horizontalOffset = options.horizontalOffset || 0; + options.verticalOffset = options.verticalOffset || 0; + + var el = overlay.el, + content = el.children[0], + content_height = content.clientHeight, + content_width = content.clientWidth; + + switch (options.verticalAlign) { + case 'top': + el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px'; + break; + default: + case 'middle': + el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px'; + break; + case 'bottom': + el.style.top = (pixel.y + options.verticalOffset) + 'px'; + break; + } + + switch (options.horizontalAlign) { + case 'left': + el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px'; + break; + default: + case 'center': + el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px'; + break; + case 'right': + el.style.left = (pixel.x + options.horizontalOffset) + 'px'; + break; + } + + el.style.display = auto_show ? 'block' : 'none'; + + if (!auto_show) { + options.show.apply(this, [el]); + } + }; + + overlay.onRemove = function() { + var el = overlay.el; + + if (options.remove) { + options.remove.apply(this, [el]); + } + else { + overlay.el.parentNode.removeChild(overlay.el); + overlay.el = null; + } + }; + + this.overlays.push(overlay); + return overlay; +}; + +GMaps.prototype.removeOverlay = function(overlay) { + for (var i = 0; i < this.overlays.length; i++) { + if (this.overlays[i] === overlay) { + this.overlays[i].setMap(null); + this.overlays.splice(i, 1); + + break; + } + } +}; + +GMaps.prototype.removeOverlays = function() { + for (var i = 0, item; item = this.overlays[i]; i++) { + item.setMap(null); + } + + this.overlays = []; +}; + +GMaps.prototype.drawPolyline = function(options) { + var path = [], + points = options.path; + + if (points.length) { + if (points[0][0] === undefined) { + path = points; + } + else { + for (var i=0, latlng; latlng=points[i]; i++) { + path.push(new google.maps.LatLng(latlng[0], latlng[1])); + } + } + } + + var polyline_options = { + map: this.map, + path: path, + strokeColor: options.strokeColor, + strokeOpacity: options.strokeOpacity, + strokeWeight: options.strokeWeight, + geodesic: options.geodesic, + clickable: true, + editable: false, + visible: true + }; + + if (options.hasOwnProperty("clickable")) { + polyline_options.clickable = options.clickable; + } + + if (options.hasOwnProperty("editable")) { + polyline_options.editable = options.editable; + } + + if (options.hasOwnProperty("icons")) { + polyline_options.icons = options.icons; + } + + if (options.hasOwnProperty("zIndex")) { + polyline_options.zIndex = options.zIndex; + } + + var polyline = new google.maps.Polyline(polyline_options); + + var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; + + for (var ev = 0; ev < polyline_events.length; ev++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(e){ + options[name].apply(this, [e]); + }); + } + })(polyline, polyline_events[ev]); + } + + this.polylines.push(polyline); + + GMaps.fire('polyline_added', polyline, this); + + return polyline; +}; + +GMaps.prototype.removePolyline = function(polyline) { + for (var i = 0; i < this.polylines.length; i++) { + if (this.polylines[i] === polyline) { + this.polylines[i].setMap(null); + this.polylines.splice(i, 1); + + GMaps.fire('polyline_removed', polyline, this); + + break; + } + } +}; + +GMaps.prototype.removePolylines = function() { + for (var i = 0, item; item = this.polylines[i]; i++) { + item.setMap(null); + } + + this.polylines = []; +}; + +GMaps.prototype.drawCircle = function(options) { + options = extend_object({ + map: this.map, + center: new google.maps.LatLng(options.lat, options.lng) + }, options); + + delete options.lat; + delete options.lng; + + var polygon = new google.maps.Circle(options), + polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; + + for (var ev = 0; ev < polygon_events.length; ev++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(e){ + options[name].apply(this, [e]); + }); + } + })(polygon, polygon_events[ev]); + } + + this.polygons.push(polygon); + + return polygon; +}; + +GMaps.prototype.drawRectangle = function(options) { + options = extend_object({ + map: this.map + }, options); + + var latLngBounds = new google.maps.LatLngBounds( + new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]), + new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1]) + ); + + options.bounds = latLngBounds; + + var polygon = new google.maps.Rectangle(options), + polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; + + for (var ev = 0; ev < polygon_events.length; ev++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(e){ + options[name].apply(this, [e]); + }); + } + })(polygon, polygon_events[ev]); + } + + this.polygons.push(polygon); + + return polygon; +}; + +GMaps.prototype.drawPolygon = function(options) { + var useGeoJSON = false; + + if(options.hasOwnProperty("useGeoJSON")) { + useGeoJSON = options.useGeoJSON; + } + + delete options.useGeoJSON; + + options = extend_object({ + map: this.map + }, options); + + if (useGeoJSON == false) { + options.paths = [options.paths.slice(0)]; + } + + if (options.paths.length > 0) { + if (options.paths[0].length > 0) { + options.paths = array_flat(array_map(options.paths, arrayToLatLng, useGeoJSON)); + } + } + + var polygon = new google.maps.Polygon(options), + polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; + + for (var ev = 0; ev < polygon_events.length; ev++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(e){ + options[name].apply(this, [e]); + }); + } + })(polygon, polygon_events[ev]); + } + + this.polygons.push(polygon); + + GMaps.fire('polygon_added', polygon, this); + + return polygon; +}; + +GMaps.prototype.removePolygon = function(polygon) { + for (var i = 0; i < this.polygons.length; i++) { + if (this.polygons[i] === polygon) { + this.polygons[i].setMap(null); + this.polygons.splice(i, 1); + + GMaps.fire('polygon_removed', polygon, this); + + break; + } + } +}; + +GMaps.prototype.removePolygons = function() { + for (var i = 0, item; item = this.polygons[i]; i++) { + item.setMap(null); + } + + this.polygons = []; +}; + +GMaps.prototype.getFromFusionTables = function(options) { + var events = options.events; + + delete options.events; + + var fusion_tables_options = options, + layer = new google.maps.FusionTablesLayer(fusion_tables_options); + + for (var ev in events) { + (function(object, name) { + google.maps.event.addListener(object, name, function(e) { + events[name].apply(this, [e]); + }); + })(layer, ev); + } + + this.layers.push(layer); + + return layer; +}; + +GMaps.prototype.loadFromFusionTables = function(options) { + var layer = this.getFromFusionTables(options); + layer.setMap(this.map); + + return layer; +}; + +GMaps.prototype.getFromKML = function(options) { + var url = options.url, + events = options.events; + + delete options.url; + delete options.events; + + var kml_options = options, + layer = new google.maps.KmlLayer(url, kml_options); + + for (var ev in events) { + (function(object, name) { + google.maps.event.addListener(object, name, function(e) { + events[name].apply(this, [e]); + }); + })(layer, ev); + } + + this.layers.push(layer); + + return layer; +}; + +GMaps.prototype.loadFromKML = function(options) { + var layer = this.getFromKML(options); + layer.setMap(this.map); + + return layer; +}; + +GMaps.prototype.addLayer = function(layerName, options) { + //var default_layers = ['weather', 'clouds', 'traffic', 'transit', 'bicycling', 'panoramio', 'places']; + options = options || {}; + var layer; + + switch(layerName) { + case 'weather': this.singleLayers.weather = layer = new google.maps.weather.WeatherLayer(); + break; + case 'clouds': this.singleLayers.clouds = layer = new google.maps.weather.CloudLayer(); + break; + case 'traffic': this.singleLayers.traffic = layer = new google.maps.TrafficLayer(); + break; + case 'transit': this.singleLayers.transit = layer = new google.maps.TransitLayer(); + break; + case 'bicycling': this.singleLayers.bicycling = layer = new google.maps.BicyclingLayer(); + break; + case 'panoramio': + this.singleLayers.panoramio = layer = new google.maps.panoramio.PanoramioLayer(); + layer.setTag(options.filter); + delete options.filter; + + //click event + if (options.click) { + google.maps.event.addListener(layer, 'click', function(event) { + options.click(event); + delete options.click; + }); + } + break; + case 'places': + this.singleLayers.places = layer = new google.maps.places.PlacesService(this.map); + + //search, nearbySearch, radarSearch callback, Both are the same + if (options.search || options.nearbySearch || options.radarSearch) { + var placeSearchRequest = { + bounds : options.bounds || null, + keyword : options.keyword || null, + location : options.location || null, + name : options.name || null, + radius : options.radius || null, + rankBy : options.rankBy || null, + types : options.types || null + }; + + if (options.radarSearch) { + layer.radarSearch(placeSearchRequest, options.radarSearch); + } + + if (options.search) { + layer.search(placeSearchRequest, options.search); + } + + if (options.nearbySearch) { + layer.nearbySearch(placeSearchRequest, options.nearbySearch); + } + } + + //textSearch callback + if (options.textSearch) { + var textSearchRequest = { + bounds : options.bounds || null, + location : options.location || null, + query : options.query || null, + radius : options.radius || null + }; + + layer.textSearch(textSearchRequest, options.textSearch); + } + break; + } + + if (layer !== undefined) { + if (typeof layer.setOptions == 'function') { + layer.setOptions(options); + } + if (typeof layer.setMap == 'function') { + layer.setMap(this.map); + } + + return layer; + } +}; + +GMaps.prototype.removeLayer = function(layer) { + if (typeof(layer) == "string" && this.singleLayers[layer] !== undefined) { + this.singleLayers[layer].setMap(null); + + delete this.singleLayers[layer]; + } + else { + for (var i = 0; i < this.layers.length; i++) { + if (this.layers[i] === layer) { + this.layers[i].setMap(null); + this.layers.splice(i, 1); + + break; + } + } + } +}; + +var travelMode, unitSystem; + +GMaps.prototype.getRoutes = function(options) { + switch (options.travelMode) { + case 'bicycling': + travelMode = google.maps.TravelMode.BICYCLING; + break; + case 'transit': + travelMode = google.maps.TravelMode.TRANSIT; + break; + case 'driving': + travelMode = google.maps.TravelMode.DRIVING; + break; + default: + travelMode = google.maps.TravelMode.WALKING; + break; + } + + if (options.unitSystem === 'imperial') { + unitSystem = google.maps.UnitSystem.IMPERIAL; + } + else { + unitSystem = google.maps.UnitSystem.METRIC; + } + + var base_options = { + avoidHighways: false, + avoidTolls: false, + optimizeWaypoints: false, + waypoints: [] + }, + request_options = extend_object(base_options, options); + + request_options.origin = /string/.test(typeof options.origin) ? options.origin : new google.maps.LatLng(options.origin[0], options.origin[1]); + request_options.destination = /string/.test(typeof options.destination) ? options.destination : new google.maps.LatLng(options.destination[0], options.destination[1]); + request_options.travelMode = travelMode; + request_options.unitSystem = unitSystem; + + delete request_options.callback; + delete request_options.error; + + var self = this, + service = new google.maps.DirectionsService(); + + service.route(request_options, function(result, status) { + if (status === google.maps.DirectionsStatus.OK) { + for (var r in result.routes) { + if (result.routes.hasOwnProperty(r)) { + self.routes.push(result.routes[r]); + } + } + + if (options.callback) { + options.callback(self.routes); + } + } + else { + if (options.error) { + options.error(result, status); + } + } + }); +}; + +GMaps.prototype.removeRoutes = function() { + this.routes = []; +}; + +GMaps.prototype.getElevations = function(options) { + options = extend_object({ + locations: [], + path : false, + samples : 256 + }, options); + + if (options.locations.length > 0) { + if (options.locations[0].length > 0) { + options.locations = array_flat(array_map([options.locations], arrayToLatLng, false)); + } + } + + var callback = options.callback; + delete options.callback; + + var service = new google.maps.ElevationService(); + + //location request + if (!options.path) { + delete options.path; + delete options.samples; + + service.getElevationForLocations(options, function(result, status) { + if (callback && typeof(callback) === "function") { + callback(result, status); + } + }); + //path request + } else { + var pathRequest = { + path : options.locations, + samples : options.samples + }; + + service.getElevationAlongPath(pathRequest, function(result, status) { + if (callback && typeof(callback) === "function") { + callback(result, status); + } + }); + } +}; + +GMaps.prototype.cleanRoute = GMaps.prototype.removePolylines; + +GMaps.prototype.drawRoute = function(options) { + var self = this; + + this.getRoutes({ + origin: options.origin, + destination: options.destination, + travelMode: options.travelMode, + waypoints: options.waypoints, + unitSystem: options.unitSystem, + error: options.error, + callback: function(e) { + if (e.length > 0) { + self.drawPolyline({ + path: e[e.length - 1].overview_path, + strokeColor: options.strokeColor, + strokeOpacity: options.strokeOpacity, + strokeWeight: options.strokeWeight + }); + + if (options.callback) { + options.callback(e[e.length - 1]); + } + } + } + }); +}; + +GMaps.prototype.travelRoute = function(options) { + if (options.origin && options.destination) { + this.getRoutes({ + origin: options.origin, + destination: options.destination, + travelMode: options.travelMode, + waypoints : options.waypoints, + unitSystem: options.unitSystem, + error: options.error, + callback: function(e) { + //start callback + if (e.length > 0 && options.start) { + options.start(e[e.length - 1]); + } + + //step callback + if (e.length > 0 && options.step) { + var route = e[e.length - 1]; + if (route.legs.length > 0) { + var steps = route.legs[0].steps; + for (var i=0, step; step=steps[i]; i++) { + step.step_number = i; + options.step(step, (route.legs[0].steps.length - 1)); + } + } + } + + //end callback + if (e.length > 0 && options.end) { + options.end(e[e.length - 1]); + } + } + }); + } + else if (options.route) { + if (options.route.legs.length > 0) { + var steps = options.route.legs[0].steps; + for (var i=0, step; step=steps[i]; i++) { + step.step_number = i; + options.step(step); + } + } + } +}; + +GMaps.prototype.drawSteppedRoute = function(options) { + var self = this; + + if (options.origin && options.destination) { + this.getRoutes({ + origin: options.origin, + destination: options.destination, + travelMode: options.travelMode, + waypoints : options.waypoints, + error: options.error, + callback: function(e) { + //start callback + if (e.length > 0 && options.start) { + options.start(e[e.length - 1]); + } + + //step callback + if (e.length > 0 && options.step) { + var route = e[e.length - 1]; + if (route.legs.length > 0) { + var steps = route.legs[0].steps; + for (var i=0, step; step=steps[i]; i++) { + step.step_number = i; + self.drawPolyline({ + path: step.path, + strokeColor: options.strokeColor, + strokeOpacity: options.strokeOpacity, + strokeWeight: options.strokeWeight + }); + options.step(step, (route.legs[0].steps.length - 1)); + } + } + } + + //end callback + if (e.length > 0 && options.end) { + options.end(e[e.length - 1]); + } + } + }); + } + else if (options.route) { + if (options.route.legs.length > 0) { + var steps = options.route.legs[0].steps; + for (var i=0, step; step=steps[i]; i++) { + step.step_number = i; + self.drawPolyline({ + path: step.path, + strokeColor: options.strokeColor, + strokeOpacity: options.strokeOpacity, + strokeWeight: options.strokeWeight + }); + options.step(step); + } + } + } +}; + +GMaps.Route = function(options) { + this.origin = options.origin; + this.destination = options.destination; + this.waypoints = options.waypoints; + + this.map = options.map; + this.route = options.route; + this.step_count = 0; + this.steps = this.route.legs[0].steps; + this.steps_length = this.steps.length; + + this.polyline = this.map.drawPolyline({ + path: new google.maps.MVCArray(), + strokeColor: options.strokeColor, + strokeOpacity: options.strokeOpacity, + strokeWeight: options.strokeWeight + }).getPath(); +}; + +GMaps.Route.prototype.getRoute = function(options) { + var self = this; + + this.map.getRoutes({ + origin : this.origin, + destination : this.destination, + travelMode : options.travelMode, + waypoints : this.waypoints || [], + error: options.error, + callback : function() { + self.route = e[0]; + + if (options.callback) { + options.callback.call(self); + } + } + }); +}; + +GMaps.Route.prototype.back = function() { + if (this.step_count > 0) { + this.step_count--; + var path = this.route.legs[0].steps[this.step_count].path; + + for (var p in path){ + if (path.hasOwnProperty(p)){ + this.polyline.pop(); + } + } + } +}; + +GMaps.Route.prototype.forward = function() { + if (this.step_count < this.steps_length) { + var path = this.route.legs[0].steps[this.step_count].path; + + for (var p in path){ + if (path.hasOwnProperty(p)){ + this.polyline.push(path[p]); + } + } + this.step_count++; + } +}; + +GMaps.prototype.checkGeofence = function(lat, lng, fence) { + return fence.containsLatLng(new google.maps.LatLng(lat, lng)); +}; + +GMaps.prototype.checkMarkerGeofence = function(marker, outside_callback) { + if (marker.fences) { + for (var i = 0, fence; fence = marker.fences[i]; i++) { + var pos = marker.getPosition(); + if (!this.checkGeofence(pos.lat(), pos.lng(), fence)) { + outside_callback(marker, fence); + } + } + } +}; + +GMaps.prototype.toImage = function(options) { + var options = options || {}, + static_map_options = {}; + + static_map_options['size'] = options['size'] || [this.el.clientWidth, this.el.clientHeight]; + static_map_options['lat'] = this.getCenter().lat(); + static_map_options['lng'] = this.getCenter().lng(); + + if (this.markers.length > 0) { + static_map_options['markers'] = []; + + for (var i = 0; i < this.markers.length; i++) { + static_map_options['markers'].push({ + lat: this.markers[i].getPosition().lat(), + lng: this.markers[i].getPosition().lng() + }); + } + } + + if (this.polylines.length > 0) { + var polyline = this.polylines[0]; + + static_map_options['polyline'] = {}; + static_map_options['polyline']['path'] = google.maps.geometry.encoding.encodePath(polyline.getPath()); + static_map_options['polyline']['strokeColor'] = polyline.strokeColor + static_map_options['polyline']['strokeOpacity'] = polyline.strokeOpacity + static_map_options['polyline']['strokeWeight'] = polyline.strokeWeight + } + + return GMaps.staticMapURL(static_map_options); +}; + +GMaps.staticMapURL = function(options){ + var parameters = [], + data, + static_root = 'https://maps.googleapis.com/maps/api/staticmap'; + + if (options.url) { + static_root = options.url; + delete options.url; + } + + static_root += '?'; + + var markers = options.markers; + + delete options.markers; + + if (!markers && options.marker) { + markers = [options.marker]; + delete options.marker; + } + + var styles = options.styles; + + delete options.styles; + + var polyline = options.polyline; + delete options.polyline; + + /** Map options **/ + if (options.center) { + parameters.push('center=' + options.center); + delete options.center; + } + else if (options.address) { + parameters.push('center=' + options.address); + delete options.address; + } + else if (options.lat) { + parameters.push(['center=', options.lat, ',', options.lng].join('')); + delete options.lat; + delete options.lng; + } + else if (options.visible) { + var visible = encodeURI(options.visible.join('|')); + parameters.push('visible=' + visible); + } + + var size = options.size; + if (size) { + if (size.join) { + size = size.join('x'); + } + delete options.size; + } + else { + size = '630x300'; + } + parameters.push('size=' + size); + + if (!options.zoom && options.zoom !== false) { + options.zoom = 15; + } + + var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true; + delete options.sensor; + parameters.push('sensor=' + sensor); + + for (var param in options) { + if (options.hasOwnProperty(param)) { + parameters.push(param + '=' + options[param]); + } + } + + /** Markers **/ + if (markers) { + var marker, loc; + + for (var i=0; data=markers[i]; i++) { + marker = []; + + if (data.size && data.size !== 'normal') { + marker.push('size:' + data.size); + delete data.size; + } + else if (data.icon) { + marker.push('icon:' + encodeURI(data.icon)); + delete data.icon; + } + + if (data.color) { + marker.push('color:' + data.color.replace('#', '0x')); + delete data.color; + } + + if (data.label) { + marker.push('label:' + data.label[0].toUpperCase()); + delete data.label; + } + + loc = (data.address ? data.address : data.lat + ',' + data.lng); + delete data.address; + delete data.lat; + delete data.lng; + + for(var param in data){ + if (data.hasOwnProperty(param)) { + marker.push(param + ':' + data[param]); + } + } + + if (marker.length || i === 0) { + marker.push(loc); + marker = marker.join('|'); + parameters.push('markers=' + encodeURI(marker)); + } + // New marker without styles + else { + marker = parameters.pop() + encodeURI('|' + loc); + parameters.push(marker); + } + } + } + + /** Map Styles **/ + if (styles) { + for (var i = 0; i < styles.length; i++) { + var styleRule = []; + if (styles[i].featureType){ + styleRule.push('feature:' + styles[i].featureType.toLowerCase()); + } + + if (styles[i].elementType) { + styleRule.push('element:' + styles[i].elementType.toLowerCase()); + } + + for (var j = 0; j < styles[i].stylers.length; j++) { + for (var p in styles[i].stylers[j]) { + var ruleArg = styles[i].stylers[j][p]; + if (p == 'hue' || p == 'color') { + ruleArg = '0x' + ruleArg.substring(1); + } + styleRule.push(p + ':' + ruleArg); + } + } + + var rule = styleRule.join('|'); + if (rule != '') { + parameters.push('style=' + rule); + } + } + } + + /** Polylines **/ + function parseColor(color, opacity) { + if (color[0] === '#'){ + color = color.replace('#', '0x'); + + if (opacity) { + opacity = parseFloat(opacity); + opacity = Math.min(1, Math.max(opacity, 0)); + if (opacity === 0) { + return '0x00000000'; + } + opacity = (opacity * 255).toString(16); + if (opacity.length === 1) { + opacity += opacity; + } + + color = color.slice(0,8) + opacity; + } + } + return color; + } + + if (polyline) { + data = polyline; + polyline = []; + + if (data.strokeWeight) { + polyline.push('weight:' + parseInt(data.strokeWeight, 10)); + } + + if (data.strokeColor) { + var color = parseColor(data.strokeColor, data.strokeOpacity); + polyline.push('color:' + color); + } + + if (data.fillColor) { + var fillcolor = parseColor(data.fillColor, data.fillOpacity); + polyline.push('fillcolor:' + fillcolor); + } + + var path = data.path; + if (path.join) { + for (var j=0, pos; pos=path[j]; j++) { + polyline.push(pos.join(',')); + } + } + else { + polyline.push('enc:' + path); + } + + polyline = polyline.join('|'); + parameters.push('path=' + encodeURI(polyline)); + } + + /** Retina support **/ + var dpi = window.devicePixelRatio || 1; + parameters.push('scale=' + dpi); + + parameters = parameters.join('&'); + return static_root + parameters; +}; + +GMaps.prototype.addMapType = function(mapTypeId, options) { + if (options.hasOwnProperty("getTileUrl") && typeof(options["getTileUrl"]) == "function") { + options.tileSize = options.tileSize || new google.maps.Size(256, 256); + + var mapType = new google.maps.ImageMapType(options); + + this.map.mapTypes.set(mapTypeId, mapType); + } + else { + throw "'getTileUrl' function required."; + } +}; + +GMaps.prototype.addOverlayMapType = function(options) { + if (options.hasOwnProperty("getTile") && typeof(options["getTile"]) == "function") { + var overlayMapTypeIndex = options.index; + + delete options.index; + + this.map.overlayMapTypes.insertAt(overlayMapTypeIndex, options); + } + else { + throw "'getTile' function required."; + } +}; + +GMaps.prototype.removeOverlayMapType = function(overlayMapTypeIndex) { + this.map.overlayMapTypes.removeAt(overlayMapTypeIndex); +}; + +GMaps.prototype.addStyle = function(options) { + var styledMapType = new google.maps.StyledMapType(options.styles, { name: options.styledMapName }); + + this.map.mapTypes.set(options.mapTypeId, styledMapType); +}; + +GMaps.prototype.setStyle = function(mapTypeId) { + this.map.setMapTypeId(mapTypeId); +}; + +GMaps.prototype.createPanorama = function(streetview_options) { + if (!streetview_options.hasOwnProperty('lat') || !streetview_options.hasOwnProperty('lng')) { + streetview_options.lat = this.getCenter().lat(); + streetview_options.lng = this.getCenter().lng(); + } + + this.panorama = GMaps.createPanorama(streetview_options); + + this.map.setStreetView(this.panorama); + + return this.panorama; +}; + +GMaps.createPanorama = function(options) { + var el = getElementById(options.el, options.context); + + options.position = new google.maps.LatLng(options.lat, options.lng); + + delete options.el; + delete options.context; + delete options.lat; + delete options.lng; + + var streetview_events = ['closeclick', 'links_changed', 'pano_changed', 'position_changed', 'pov_changed', 'resize', 'visible_changed'], + streetview_options = extend_object({visible : true}, options); + + for (var i = 0; i < streetview_events.length; i++) { + delete streetview_options[streetview_events[i]]; + } + + var panorama = new google.maps.StreetViewPanorama(el, streetview_options); + + for (var i = 0; i < streetview_events.length; i++) { + (function(object, name) { + if (options[name]) { + google.maps.event.addListener(object, name, function(){ + options[name].apply(this); + }); + } + })(panorama, streetview_events[i]); + } + + return panorama; +}; + +GMaps.prototype.on = function(event_name, handler) { + return GMaps.on(event_name, this, handler); +}; + +GMaps.prototype.off = function(event_name) { + GMaps.off(event_name, this); +}; + +GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed']; + +GMaps.on = function(event_name, object, handler) { + if (GMaps.custom_events.indexOf(event_name) == -1) { + if(object instanceof GMaps) object = object.map; + return google.maps.event.addListener(object, event_name, handler); + } + else { + var registered_event = { + handler : handler, + eventName : event_name + }; + + object.registered_events[event_name] = object.registered_events[event_name] || []; + object.registered_events[event_name].push(registered_event); + + return registered_event; + } +}; + +GMaps.off = function(event_name, object) { + if (GMaps.custom_events.indexOf(event_name) == -1) { + if(object instanceof GMaps) object = object.map; + google.maps.event.clearListeners(object, event_name); + } + else { + object.registered_events[event_name] = []; + } +}; + +GMaps.fire = function(event_name, object, scope) { + if (GMaps.custom_events.indexOf(event_name) == -1) { + google.maps.event.trigger(object, event_name, Array.prototype.slice.apply(arguments).slice(2)); + } + else { + if(event_name in scope.registered_events) { + var firing_events = scope.registered_events[event_name]; + + for(var i = 0; i < firing_events.length; i++) { + (function(handler, scope, object) { + handler.apply(scope, [object]); + })(firing_events[i]['handler'], scope, object); + } + } + } +}; + +GMaps.geolocate = function(options) { + var complete_callback = options.always || options.complete; + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function(position) { + options.success(position); + + if (complete_callback) { + complete_callback(); + } + }, function(error) { + options.error(error); + + if (complete_callback) { + complete_callback(); + } + }, options.options); + } + else { + options.not_supported(); + + if (complete_callback) { + complete_callback(); + } + } +}; + +GMaps.geocode = function(options) { + this.geocoder = new google.maps.Geocoder(); + var callback = options.callback; + if (options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) { + options.latLng = new google.maps.LatLng(options.lat, options.lng); + } + + delete options.lat; + delete options.lng; + delete options.callback; + + this.geocoder.geocode(options, function(results, status) { + callback(results, status); + }); +}; + +//========================== +// Polygon containsLatLng +// https://github.com/tparkin/Google-Maps-Point-in-Polygon +// Poygon getBounds extension - google-maps-extensions +// http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js +if (!google.maps.Polygon.prototype.getBounds) { + google.maps.Polygon.prototype.getBounds = function(latLng) { + var bounds = new google.maps.LatLngBounds(); + var paths = this.getPaths(); + var path; + + for (var p = 0; p < paths.getLength(); p++) { + path = paths.getAt(p); + for (var i = 0; i < path.getLength(); i++) { + bounds.extend(path.getAt(i)); + } + } + + return bounds; + }; +} + +if (!google.maps.Polygon.prototype.containsLatLng) { + // Polygon containsLatLng - method to determine if a latLng is within a polygon + google.maps.Polygon.prototype.containsLatLng = function(latLng) { + // Exclude points outside of bounds as there is no way they are in the poly + var bounds = this.getBounds(); + + if (bounds !== null && !bounds.contains(latLng)) { + return false; + } + + // Raycast point in polygon method + var inPoly = false; + + var numPaths = this.getPaths().getLength(); + for (var p = 0; p < numPaths; p++) { + var path = this.getPaths().getAt(p); + var numPoints = path.getLength(); + var j = numPoints - 1; + + for (var i = 0; i < numPoints; i++) { + var vertex1 = path.getAt(i); + var vertex2 = path.getAt(j); + + if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) { + if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) { + inPoly = !inPoly; + } + } + + j = i; + } + } + + return inPoly; + }; +} + +if (!google.maps.Circle.prototype.containsLatLng) { + google.maps.Circle.prototype.containsLatLng = function(latLng) { + if (google.maps.geometry) { + return google.maps.geometry.spherical.computeDistanceBetween(this.getCenter(), latLng) <= this.getRadius(); + } + else { + return true; + } + }; +} + +google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) { + return this.contains(latLng); +}; + +google.maps.Marker.prototype.setFences = function(fences) { + this.fences = fences; +}; + +google.maps.Marker.prototype.addFence = function(fence) { + this.fences.push(fence); +}; + +google.maps.Marker.prototype.getId = function() { + return this['__gm_id']; +}; + +//========================== +// Array indexOf +// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + "use strict"; + if (this == null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n != 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + } +} + +return GMaps; +})); diff --git a/public/js/owl.carousel.min.js b/public/js/owl.carousel.min.js new file mode 100644 index 000000000..98ae16ecf --- /dev/null +++ b/public/js/owl.carousel.min.js @@ -0,0 +1 @@ +eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7(A 3c.3q!=="9"){3c.3q=9(e){9 t(){}t.5S=e;p 5R t}}(9(e,t,n){h r={1N:9(t,n){h r=c;r.$k=e(n);r.6=e.4M({},e.37.2B.6,r.$k.v(),t);r.2A=t;r.4L()},4L:9(){9 r(e){h n,r="";7(A t.6.33==="9"){t.6.33.R(c,[e])}l{1A(n 38 e.d){7(e.d.5M(n)){r+=e.d[n].1K}}t.$k.2y(r)}t.3t()}h t=c,n;7(A t.6.2H==="9"){t.6.2H.R(c,[t.$k])}7(A t.6.2O==="2Y"){n=t.6.2O;e.5K(n,r)}l{t.3t()}},3t:9(){h e=c;e.$k.v("d-4I",e.$k.2x("2w")).v("d-4F",e.$k.2x("H"));e.$k.z({2u:0});e.2t=e.6.q;e.4E();e.5v=0;e.1X=14;e.23()},23:9(){h e=c;7(e.$k.25().N===0){p b}e.1M();e.4C();e.$S=e.$k.25();e.E=e.$S.N;e.4B();e.$G=e.$k.17(".d-1K");e.$K=e.$k.17(".d-1p");e.3u="U";e.13=0;e.26=[0];e.m=0;e.4A();e.4z()},4z:9(){h e=c;e.2V();e.2W();e.4t();e.30();e.4r();e.4q();e.2p();e.4o();7(e.6.2o!==b){e.4n(e.6.2o)}7(e.6.O===j){e.6.O=4Q}e.19();e.$k.17(".d-1p").z("4i","4h");7(!e.$k.2m(":3n")){e.3o()}l{e.$k.z("2u",1)}e.5O=b;e.2l();7(A e.6.3s==="9"){e.6.3s.R(c,[e.$k])}},2l:9(){h e=c;7(e.6.1Z===j){e.1Z()}7(e.6.1B===j){e.1B()}e.4g();7(A e.6.3w==="9"){e.6.3w.R(c,[e.$k])}},3x:9(){h e=c;7(A e.6.3B==="9"){e.6.3B.R(c,[e.$k])}e.3o();e.2V();e.2W();e.4f();e.30();e.2l();7(A e.6.3D==="9"){e.6.3D.R(c,[e.$k])}},3F:9(){h e=c;t.1c(9(){e.3x()},0)},3o:9(){h e=c;7(e.$k.2m(":3n")===b){e.$k.z({2u:0});t.18(e.1C);t.18(e.1X)}l{p b}e.1X=t.4d(9(){7(e.$k.2m(":3n")){e.3F();e.$k.4b({2u:1},2M);t.18(e.1X)}},5x)},4B:9(){h e=c;e.$S.5n(\'\').4a(\'\');e.$k.17(".d-1p").4a(\'\');e.1H=e.$k.17(".d-1p-49");e.$k.z("4i","4h")},1M:9(){h e=c,t=e.$k.1I(e.6.1M),n=e.$k.1I(e.6.2i);7(!t){e.$k.I(e.6.1M)}7(!n){e.$k.I(e.6.2i)}},2V:9(){h t=c,n,r;7(t.6.2Z===b){p b}7(t.6.48===j){t.6.q=t.2t=1;t.6.1h=b;t.6.1s=b;t.6.1O=b;t.6.22=b;t.6.1Q=b;t.6.1R=b;p b}n=e(t.6.47).1f();7(n>(t.6.1s[0]||t.2t)){t.6.q=t.2t}7(t.6.1h!==b){t.6.1h.5g(9(e,t){p e[0]-t[0]});1A(r=0;rt.E&&t.6.46===j){t.6.q=t.E}},4r:9(){h n=c,r,i;7(n.6.2Z!==j){p b}i=e(t).1f();n.3d=9(){7(e(t).1f()!==i){7(n.6.O!==b){t.18(n.1C)}t.5d(r);r=t.1c(9(){i=e(t).1f();n.3x()},n.6.45)}};e(t).44(n.3d)},4f:9(){h e=c;e.2g(e.m);7(e.6.O!==b){e.3j()}},43:9(){h t=c,n=0,r=t.E-t.6.q;t.$G.2f(9(i){h s=e(c);s.z({1f:t.M}).v("d-1K",3p(i));7(i%t.6.q===0||i===r){7(!(i>r)){n+=1}}s.v("d-24",n)})},42:9(){h e=c,t=e.$G.N*e.M;e.$K.z({1f:t*2,T:0});e.43()},2W:9(){h e=c;e.40();e.42();e.3Z();e.3v()},40:9(){h e=c;e.M=1F.4O(e.$k.1f()/e.6.q)},3v:9(){h e=c,t=(e.E*e.M-e.6.q*e.M)*-1;7(e.6.q>e.E){e.D=0;t=0;e.3z=0}l{e.D=e.E-e.6.q;e.3z=t}p t},3Y:9(){p 0},3Z:9(){h t=c,n=0,r=0,i,s,o;t.J=[0];t.3E=[];1A(i=0;i\').5m("5l",!t.F.15).5c(t.$k)}7(t.6.1v===j){t.3T()}7(t.6.2a===j){t.3S()}},3S:9(){h t=c,n=e(\'\');t.B.1o(n);t.1u=e("",{"H":"d-1n",2y:t.6.2U[0]||""});t.1q=e("",{"H":"d-U",2y:t.6.2U[1]||""});n.1o(t.1u).1o(t.1q);n.w("2X.B 21.B",\'L[H^="d"]\',9(e){e.1l()});n.w("2n.B 28.B",\'L[H^="d"]\',9(n){n.1l();7(e(c).1I("d-U")){t.U()}l{t.1n()}})},3T:9(){h t=c;t.1k=e(\'\');t.B.1o(t.1k);t.1k.w("2n.B 28.B",".d-1j",9(n){n.1l();7(3p(e(c).v("d-1j"))!==t.m){t.1g(3p(e(c).v("d-1j")),j)}})},3P:9(){h t=c,n,r,i,s,o,u;7(t.6.1v===b){p b}t.1k.2y("");n=0;r=t.E-t.E%t.6.q;1A(s=0;s",{"H":"d-1j"});u=e("<3N>",{4R:t.6.39===j?n:"","H":t.6.39===j?"d-59":""});o.1o(u);o.v("d-1j",r===s?i:s);o.v("d-24",n);t.1k.1o(o)}}t.35()},35:9(){h t=c;7(t.6.1v===b){p b}t.1k.17(".d-1j").2f(9(){7(e(c).v("d-24")===e(t.$G[t.m]).v("d-24")){t.1k.17(".d-1j").Z("2d");e(c).I("2d")}})},3e:9(){h e=c;7(e.6.2a===b){p b}7(e.6.2e===b){7(e.m===0&&e.D===0){e.1u.I("1b");e.1q.I("1b")}l 7(e.m===0&&e.D!==0){e.1u.I("1b");e.1q.Z("1b")}l 7(e.m===e.D){e.1u.Z("1b");e.1q.I("1b")}l 7(e.m!==0&&e.m!==e.D){e.1u.Z("1b");e.1q.Z("1b")}}},30:9(){h e=c;e.3P();e.3e();7(e.B){7(e.6.q>=e.E){e.B.3K()}l{e.B.3J()}}},55:9(){h e=c;7(e.B){e.B.3k()}},U:9(e){h t=c;7(t.1E){p b}t.m+=t.6.12===j?t.6.q:1;7(t.m>t.D+(t.6.12===j?t.6.q-1:0)){7(t.6.2e===j){t.m=0;e="2k"}l{t.m=t.D;p b}}t.1g(t.m,e)},1n:9(e){h t=c;7(t.1E){p b}7(t.6.12===j&&t.m>0&&t.m=i.D){e=i.D}l 7(e<=0){e=0}i.m=i.d.m=e;7(i.6.2o!==b&&r!=="4e"&&i.6.q===1&&i.F.1x===j){i.1t(0);7(i.F.1x===j){i.1L(i.J[e])}l{i.1r(i.J[e],1)}i.2r();i.4l();p b}s=i.J[e];7(i.F.1x===j){i.1T=b;7(n===j){i.1t("1w");t.1c(9(){i.1T=j},i.6.1w)}l 7(n==="2k"){i.1t(i.6.2v);t.1c(9(){i.1T=j},i.6.2v)}l{i.1t("1m");t.1c(9(){i.1T=j},i.6.1m)}i.1L(s)}l{7(n===j){i.1r(s,i.6.1w)}l 7(n==="2k"){i.1r(s,i.6.2v)}l{i.1r(s,i.6.1m)}}i.2r()},2g:9(e){h t=c;7(A t.6.1Y==="9"){t.6.1Y.R(c,[t.$k])}7(e>=t.D||e===-1){e=t.D}l 7(e<=0){e=0}t.1t(0);7(t.F.1x===j){t.1L(t.J[e])}l{t.1r(t.J[e],1)}t.m=t.d.m=e;t.2r()},2r:9(){h e=c;e.26.2D(e.m);e.13=e.d.13=e.26[e.26.N-2];e.26.5f(0);7(e.13!==e.m){e.35();e.3e();e.2l();7(e.6.O!==b){e.3j()}}7(A e.6.3y==="9"&&e.13!==e.m){e.6.3y.R(c,[e.$k])}},X:9(){h e=c;e.3A="X";t.18(e.1C)},3j:9(){h e=c;7(e.3A!=="X"){e.19()}},19:9(){h e=c;e.3A="19";7(e.6.O===b){p b}t.18(e.1C);e.1C=t.4d(9(){e.U(j)},e.6.O)},1t:9(e){h t=c;7(e==="1m"){t.$K.z(t.2z(t.6.1m))}l 7(e==="1w"){t.$K.z(t.2z(t.6.1w))}l 7(A e!=="2Y"){t.$K.z(t.2z(e))}},2z:9(e){p{"-1G-1a":"2C "+e+"1z 2s","-1W-1a":"2C "+e+"1z 2s","-o-1a":"2C "+e+"1z 2s",1a:"2C "+e+"1z 2s"}},3H:9(){p{"-1G-1a":"","-1W-1a":"","-o-1a":"",1a:""}},3I:9(e){p{"-1G-P":"1i("+e+"V, C, C)","-1W-P":"1i("+e+"V, C, C)","-o-P":"1i("+e+"V, C, C)","-1z-P":"1i("+e+"V, C, C)",P:"1i("+e+"V, C,C)"}},1L:9(e){h t=c;t.$K.z(t.3I(e))},3L:9(e){h t=c;t.$K.z({T:e})},1r:9(e,t){h n=c;n.29=b;n.$K.X(j,j).4b({T:e},{54:t||n.6.1m,3M:9(){n.29=j}})},4E:9(){h e=c,r="1i(C, C, C)",i=n.56("L"),s,o,u,a;i.2w.3O=" -1W-P:"+r+"; -1z-P:"+r+"; -o-P:"+r+"; -1G-P:"+r+"; P:"+r;s=/1i\\(C, C, C\\)/g;o=i.2w.3O.5i(s);u=o!==14&&o.N===1;a="5z"38 t||t.5Q.4P;e.F={1x:u,15:a}},4q:9(){h e=c;7(e.6.27!==b||e.6.1U!==b){e.3Q();e.3R()}},4C:9(){h e=c,t=["s","e","x"];e.16={};7(e.6.27===j&&e.6.1U===j){t=["2X.d 21.d","2N.d 3U.d","2n.d 3V.d 28.d"]}l 7(e.6.27===b&&e.6.1U===j){t=["2X.d","2N.d","2n.d 3V.d"]}l 7(e.6.27===j&&e.6.1U===b){t=["21.d","3U.d","28.d"]}e.16.3W=t[0];e.16.2K=t[1];e.16.2J=t[2]},3R:9(){h t=c;t.$k.w("5y.d",9(e){e.1l()});t.$k.w("21.3X",9(t){p e(t.1d).2m("5C, 5E, 5F, 5N")})},3Q:9(){9 s(e){7(e.2b!==W){p{x:e.2b[0].2c,y:e.2b[0].41}}7(e.2b===W){7(e.2c!==W){p{x:e.2c,y:e.41}}7(e.2c===W){p{x:e.52,y:e.53}}}}9 o(t){7(t==="w"){e(n).w(r.16.2K,a);e(n).w(r.16.2J,f)}l 7(t==="Q"){e(n).Q(r.16.2K);e(n).Q(r.16.2J)}}9 u(n){h u=n.3h||n||t.3g,a;7(u.5a===3){p b}7(r.E<=r.6.q){p}7(r.29===b&&!r.6.3f){p b}7(r.1T===b&&!r.6.3f){p b}7(r.6.O!==b){t.18(r.1C)}7(r.F.15!==j&&!r.$K.1I("3b")){r.$K.I("3b")}r.11=0;r.Y=0;e(c).z(r.3H());a=e(c).2h();i.2S=a.T;i.2R=s(u).x-a.T;i.2P=s(u).y-a.5o;o("w");i.2j=b;i.2L=u.1d||u.4c}9 a(o){h u=o.3h||o||t.3g,a,f;r.11=s(u).x-i.2R;r.2I=s(u).y-i.2P;r.Y=r.11-i.2S;7(A r.6.2E==="9"&&i.3C!==j&&r.Y!==0){i.3C=j;r.6.2E.R(r,[r.$k])}7((r.Y>8||r.Y<-8)&&r.F.15===j){7(u.1l!==W){u.1l()}l{u.5L=b}i.2j=j}7((r.2I>10||r.2I<-10)&&i.2j===b){e(n).Q("2N.d")}a=9(){p r.Y/5};f=9(){p r.3z+r.Y/5};r.11=1F.3v(1F.3Y(r.11,a()),f());7(r.F.1x===j){r.1L(r.11)}l{r.3L(r.11)}}9 f(n){h s=n.3h||n||t.3g,u,a,f;s.1d=s.1d||s.4c;i.3C=b;7(r.F.15!==j){r.$K.Z("3b")}7(r.Y<0){r.1y=r.d.1y="T"}l{r.1y=r.d.1y="3i"}7(r.Y!==0){u=r.4j();r.1g(u,b,"4e");7(i.2L===s.1d&&r.F.15!==j){e(s.1d).w("3a.4k",9(t){t.4S();t.4T();t.1l();e(t.1d).Q("3a.4k")});a=e.4N(s.1d,"4V").3a;f=a.4W();a.4X(0,0,f)}}o("Q")}h r=c,i={2R:0,2P:0,4Y:0,2S:0,2h:14,4Z:14,50:14,2j:14,51:14,2L:14};r.29=j;r.$k.w(r.16.3W,".d-1p",u)},4j:9(){h e=c,t=e.4m();7(t>e.D){e.m=e.D;t=e.D}l 7(e.11>=0){t=0;e.m=0}p t},4m:9(){h t=c,n=t.6.12===j?t.3E:t.J,r=t.11,i=14;e.2f(n,9(s,o){7(r-t.M/20>n[s+1]&&r-t.M/20(n[s+1]||n[s]-t.M)&&t.34()==="3i"){7(t.6.12===j){i=n[s+1]||n[n.N-1];t.m=e.4p(i,t.J)}l{i=n[s+1];t.m=s+1}}});p t.m},34:9(){h e=c,t;7(e.Y<0){t="3i";e.3u="U"}l{t="T";e.3u="1n"}p t},4A:9(){h e=c;e.$k.w("d.U",9(){e.U()});e.$k.w("d.1n",9(){e.1n()});e.$k.w("d.19",9(t,n){e.6.O=n;e.19();e.32="19"});e.$k.w("d.X",9(){e.X();e.32="X"});e.$k.w("d.1g",9(t,n){e.1g(n)});e.$k.w("d.2g",9(t,n){e.2g(n)})},2p:9(){h e=c;7(e.6.2p===j&&e.F.15!==j&&e.6.O!==b){e.$k.w("57",9(){e.X()});e.$k.w("58",9(){7(e.32!=="X"){e.19()}})}},1Z:9(){h t=c,n,r,i,s,o;7(t.6.1Z===b){p b}1A(n=0;n=t.m}l{o=j}7(o&&i=n.$S.N||r===-1){n.$S.1S(-1).5X(e)}l{n.$S.1S(r).5Y(e)}n.23()},5Z:9(e){h t=c,n;7(t.$k.25().N===0){p b}7(e===W||e===-1){n=-1}l{n=e}t.1V();t.$S.1S(n).3k();t.23()}};e.37.2B=9(t){p c.2f(9(){7(e(c).v("d-1N")===j){p b}e(c).v("d-1N",j);h n=3c.3q(r);n.1N(t,c);e.v(c,"2B",n)})};e.37.2B.6={q:5,1h:b,1s:[60,4],1O:[61,3],22:[62,2],1Q:b,1R:[63,1],48:b,46:b,1m:2M,1w:64,2v:65,O:b,2p:b,2a:b,2U:["1n","U"],2e:j,12:b,1v:j,39:b,2Z:j,45:2M,47:t,1M:"d-66",2i:"d-2i",1Z:b,4v:j,4x:"4y",1B:b,2O:b,33:b,3f:j,27:j,1U:j,2F:b,2o:b,3B:b,3D:b,2H:b,3s:b,1Y:b,3y:b,3w:b,2E:b,2T:b}})(67,68,69)',62,382,'||||||options|if||function||false|this|owl||||var||true|elem|else|currentItem|||return|items|||||data|on|||css|typeof|owlControls|0px|maximumItem|itemsAmount|browser|owlItems|class|addClass|positionsInArray|owlWrapper|div|itemWidth|length|autoPlay|transform|off|apply|userItems|left|next|px|undefined|stop|newRelativeX|removeClass||newPosX|scrollPerPage|prevItem|null|isTouch|ev_types|find|clearInterval|play|transition|disabled|setTimeout|target|loaded|width|goTo|itemsCustom|translate3d|page|paginationWrapper|preventDefault|slideSpeed|prev|append|wrapper|buttonNext|css2slide|itemsDesktop|swapSpeed|buttonPrev|pagination|paginationSpeed|support3d|dragDirection|ms|for|autoHeight|autoPlayInterval|visibleItems|isTransition|Math|webkit|wrapperOuter|hasClass|src|item|transition3d|baseClass|init|itemsDesktopSmall|origin|itemsTabletSmall|itemsMobile|eq|isCss3Finish|touchDrag|unWrap|moz|checkVisible|beforeMove|lazyLoad||mousedown|itemsTablet|setVars|roundPages|children|prevArr|mouseDrag|mouseup|isCssFinish|navigation|touches|pageX|active|rewindNav|each|jumpTo|position|theme|sliding|rewind|eachMoveUpdate|is|touchend|transitionStyle|stopOnHover|100|afterGo|ease|orignalItems|opacity|rewindSpeed|style|attr|html|addCssSpeed|userOptions|owlCarousel|all|push|startDragging|addClassActive|height|beforeInit|newPosY|end|move|targetElement|200|touchmove|jsonPath|offsetY|completeImg|offsetX|relativePos|afterLazyLoad|navigationText|updateItems|calculateAll|touchstart|string|responsive|updateControls|clearTransStyle|hoverStatus|jsonSuccess|moveDirection|checkPagination|endCurrent|fn|in|paginationNumbers|click|grabbing|Object|resizer|checkNavigation|dragBeforeAnimFinish|event|originalEvent|right|checkAp|remove|get|endPrev|visible|watchVisibility|Number|create|unwrap|afterInit|logIn|playDirection|max|afterAction|updateVars|afterMove|maximumPixels|apStatus|beforeUpdate|dragging|afterUpdate|pagesInArray|reload|clearEvents|removeTransition|doTranslate|show|hide|css2move|complete|span|cssText|updatePagination|gestures|disabledEvents|buildButtons|buildPagination|mousemove|touchcancel|start|disableTextSelect|min|loops|calculateWidth|pageY|appendWrapperSizes|appendItemsSizes|resize|responsiveRefreshRate|itemsScaleUp|responsiveBaseWidth|singleItem|outer|wrap|animate|srcElement|setInterval|drag|updatePosition|onVisibleItems|block|display|getNewPosition|disable|singleItemTransition|closestItem|transitionTypes|owlStatus|inArray|moveEvents|response|continue|buildControls|loading|lazyFollow|lazyPreload|lazyEffect|fade|onStartup|customEvents|wrapItems|eventTypes|naturalWidth|checkBrowser|originalClasses|outClass|inClass|originalStyles|abs|perspective|loadContent|extend|_data|round|msMaxTouchPoints|5e3|text|stopImmediatePropagation|stopPropagation|buttons|events|pop|splice|baseElWidth|minSwipe|maxSwipe|dargging|clientX|clientY|duration|destroyControls|createElement|mouseover|mouseout|numbers|which|lazyOwl|appendTo|clearTimeout|checked|shift|sort|removeAttr|match|fadeIn|400|clickable|toggleClass|wrapAll|top|prop|tagName|DIV|background|image|url|wrapperWidth|img|500|dragstart|ontouchstart|controls|out|input|relative|textarea|select|webkitAnimationEnd|oAnimationEnd|MSAnimationEnd|animationend|getJSON|returnValue|hasOwnProperty|option|onstartup|baseElement|navigator|new|prototype|destroy|removeData|reinit|addItem|after|before|removeItem|1199|979|768|479|800|1e3|carousel|jQuery|window|document'.split('|'),0,{})) \ No newline at end of file diff --git a/public/js/respond.min.js b/public/js/respond.min.js new file mode 100644 index 000000000..835b161d4 --- /dev/null +++ b/public/js/respond.min.js @@ -0,0 +1,6 @@ +/*! Respond.js v1.4.2: min/max-width media query polyfill + * Copyright 2014 Scott Jehl + * Licensed under MIT + * http://j.mp/respondjs */ + +!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b + + + https://canadiandataguy.com/ + + + + + + diff --git a/public/page/2/index.html b/public/page/2/index.html new file mode 100644 index 000000000..97d2d325d --- /dev/null +++ b/public/page/2/index.html @@ -0,0 +1,1044 @@ + + + + + + + + + +Canadian Data Guy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +

    What People Say About Me?

    +
    + +

    + +

    + + + + + + + +
    + +
    +
    +
    + + + + + + + + + + + + +
    +
    + +
    +
    +

    From our blog

    +
    + +

    + +

    + + + +
    + + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on February 23, 2023 + +

    + +

    Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage.

    +

    + Continue reading +

    + +
    +
    + + +
    + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    How to get the Job ID and Run ID for a Databricks Job

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on February 23, 2023 + +

    + +

    How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f""" job_id: {dbutils.

    +

    + Continue reading +

    + +
    +
    + + +
    + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    How to prepare yourself to be better at Data Interviews?

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on January 27, 2023 + +

    + +

    How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

    +

    + Continue reading +

    + +
    +
    + + +
    + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    How I wrote my first Spark Streaming Application with Joins?

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on January 25, 2023 + +

    + +

    How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

    +

    + Continue reading +

    + +
    +
    + + +
    + + +
    + + + + +
    +
    + +
    + + + + + + + + + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/page/3/index.html b/public/page/3/index.html new file mode 100644 index 000000000..49fe14fa3 --- /dev/null +++ b/public/page/3/index.html @@ -0,0 +1,857 @@ + + + + + + + + + +Canadian Data Guy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +

    What People Say About Me?

    +
    + +

    + +

    + + + + + + + +
    + +
    +
    +
    + + + + + + + + + + + + +
    +
    + +
    +
    +

    From our blog

    +
    + +

    + +

    + + + +
    + + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    1 on 1 Interview Coaching

    +

    + + +

    + +

    Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

    +

    + Continue reading +

    + +
    +
    + + +
    + +
    +
    +
    +
    + + + +
    +
    +
    +

    + Read more +

    +
    +
    + +
    +

    Youtube

    +

    + + +

    + +

    +

    + Continue reading +

    + +
    +
    + + +
    + + +
    + + + + +
    +
    + +
    + + + + + + + + + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 000000000..8907a8240 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,182 @@ + + + + https://canadiandataguy.com/blog/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/categories/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/tags/concurrency/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/categories/databricks/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/tags/delta/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/categories/spark/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/tags/ + 2023-09-30T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + 2023-09-15T17:29:06+00:00 + + https://canadiandataguy.com/tags/dbsql/ + 2023-09-15T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + 2023-09-12T17:29:06+00:00 + + https://canadiandataguy.com/tags/foreachbatch/ + 2023-06-06T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + 2023-06-06T17:29:06+00:00 + + https://canadiandataguy.com/tags/streaming/ + 2023-06-06T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + 2023-04-23T21:38:16+00:00 + + https://canadiandataguy.com/tags/cost-savings/ + 2023-04-23T21:38:16+00:00 + + https://canadiandataguy.com/tags/parallelization/ + 2023-04-23T21:38:16+00:00 + + https://canadiandataguy.com/tags/python/ + 2023-04-23T21:38:16+00:00 + + https://canadiandataguy.com/tags/graviton/ + 2023-04-23T17:29:06+00:00 + + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + 2023-04-23T17:29:06+00:00 + + https://canadiandataguy.com/categories/best-practices/ + 2023-04-19T01:04:45+00:00 + + https://canadiandataguy.com/categories/spark-streaming/ + 2023-04-19T01:04:45+00:00 + + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + 2023-04-19T01:04:45+00:00 + + https://canadiandataguy.com/tags/checkpoint/ + 2023-03-23T06:32:42+00:00 + + https://canadiandataguy.com/blog/spark-stream-stream-join/ + 2023-03-23T06:32:42+00:00 + + https://canadiandataguy.com/tags/spark-streaming/ + 2023-03-23T06:32:42+00:00 + + https://canadiandataguy.com/categories/streaming/ + 2023-03-23T06:32:42+00:00 + + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + 2023-03-21T06:14:44+00:00 + + https://canadiandataguy.com/tags/delta-live-tables/ + 2023-03-03T17:35:21-05:00 + + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + 2023-03-03T17:35:21-05:00 + + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + 2023-02-23T17:35:21-05:00 + + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + 2023-02-23T17:35:21-05:00 + + https://canadiandataguy.com/tags/job_id/ + 2023-02-23T17:35:21-05:00 + + https://canadiandataguy.com/tags/run_id/ + 2023-02-23T17:35:21-05:00 + + https://canadiandataguy.com/tags/workspace/ + 2023-02-23T17:35:21-05:00 + + https://canadiandataguy.com/tags/books/ + 2023-01-27T17:35:21-05:00 + + https://canadiandataguy.com/categories/coaching/ + 2023-01-27T17:35:21-05:00 + + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + 2023-01-27T17:35:21-05:00 + + https://canadiandataguy.com/categories/interviewing/ + 2023-01-27T17:35:21-05:00 + + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + 2023-01-25T17:35:21-05:00 + + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + 2023-01-25T17:35:21-05:00 + + https://canadiandataguy.com/tags/kafka/ + 2023-01-25T17:35:21-05:00 + + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + 2022-12-13T17:35:21-05:00 + + https://canadiandataguy.com/tags/merge/ + 2022-10-13T04:09:03-05:00 + + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + 2022-10-13T04:09:03-05:00 + + https://canadiandataguy.com/tags/optimize/ + 2022-10-13T04:09:03-05:00 + + https://canadiandataguy.com/tags/z-order/ + 2022-10-13T04:09:03-05:00 + + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + 2022-10-12T04:06:14-05:00 + + https://canadiandataguy.com/blog/arcresources/ + 2022-05-24T17:35:21-05:00 + + https://canadiandataguy.com/categories/customer-stories/ + 2022-05-24T17:35:21-05:00 + + https://canadiandataguy.com/tags/spark/ + 2022-05-24T17:35:21-05:00 + + https://canadiandataguy.com/blog/audantic/ + 2022-05-05T17:35:21-05:00 + + https://canadiandataguy.com/blog/1-on-1-coaching/ + + https://canadiandataguy.com/about-me/ + + https://canadiandataguy.com/authors/ + + https://canadiandataguy.com/tags/coaching/ + + https://canadiandataguy.com/contact/ + + https://canadiandataguy.com/databricks-customer-success-stories/ + + https://canadiandataguy.com/tags/interviewing/ + + https://canadiandataguy.com/tags/youtube/ + + https://canadiandataguy.com/blog/youtube/ + + diff --git a/public/tags/books/index.html b/public/tags/books/index.html new file mode 100644 index 000000000..073b6049e --- /dev/null +++ b/public/tags/books/index.html @@ -0,0 +1,766 @@ + + + + + + + + + +books + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    books

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to prepare yourself to be better at Data Interviews?

    +
    +

    + + + + in + + interviewing, + coaching + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + January 27, 2023 +

    + +
    + +

    How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python & Big Data fundamentals. Here is how to prepare for each of them.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/books/index.xml b/public/tags/books/index.xml new file mode 100644 index 000000000..5bdd8553e --- /dev/null +++ b/public/tags/books/index.xml @@ -0,0 +1,20 @@ + + + + books on Canadian Data Guy + https://canadiandataguy.com/tags/books/ + Recent content in books on Canadian Data Guy + Hugo -- gohugo.io + en-us + Fri, 27 Jan 2023 17:35:21 -0500 + + + How to prepare yourself to be better at Data Interviews? + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoprepareyourselftobebetteratdatainterviews/ + How to prepare yourself to be better at Data Interviews? In this blog, let’s talk about some specific actions you can take to perform better at Data Interviews. Below is general advice based on my experience coaching 100+ candidates and my industry experience being on both sides of the table. +Popular skill set as of 2023 still seems to be SQL, Python &amp; Big Data fundamentals. Here is how to prepare for each of them. + + + diff --git a/public/tags/books/page/1/index.html b/public/tags/books/page/1/index.html new file mode 100644 index 000000000..491f07cc8 --- /dev/null +++ b/public/tags/books/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/books/ + + + + + + diff --git a/public/tags/checkpoint/index.html b/public/tags/checkpoint/index.html new file mode 100644 index 000000000..c7b12b79a --- /dev/null +++ b/public/tags/checkpoint/index.html @@ -0,0 +1,910 @@ + + + + + + + + + +checkpoint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    checkpoint

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to write your first Spark application with Stream-Stream Joins with working code.

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 23, 2023 +

    + +
    + +

    How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Dive Deep into Spark Streaming Checkpoint

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 21, 2023 +

    + +
    + +

    From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How to upgrade your Spark Stream application with a new checkpoint!

    +
    +

    + + + + in + + streaming, + spark streaming + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + January 25, 2023 +

    + +
    + +

    How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/checkpoint/index.xml b/public/tags/checkpoint/index.xml new file mode 100644 index 000000000..51b928439 --- /dev/null +++ b/public/tags/checkpoint/index.xml @@ -0,0 +1,40 @@ + + + + checkpoint on Canadian Data Guy + https://canadiandataguy.com/tags/checkpoint/ + Recent content in checkpoint on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Mar 2023 06:32:42 +0000 + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + diff --git a/public/tags/checkpoint/page/1/index.html b/public/tags/checkpoint/page/1/index.html new file mode 100644 index 000000000..f29956646 --- /dev/null +++ b/public/tags/checkpoint/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/checkpoint/ + + + + + + diff --git a/public/tags/coaching/index.html b/public/tags/coaching/index.html new file mode 100644 index 000000000..cef23ab43 --- /dev/null +++ b/public/tags/coaching/index.html @@ -0,0 +1,768 @@ + + + + + + + + + +coaching + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    coaching

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    1 on 1 Interview Coaching

    +
    +

    + + + + in + + coaching + + + +

    + +
    + +

    Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Youtube

    +
    +

    + + + + in + + coaching + + + +

    + +
    + +

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/coaching/index.xml b/public/tags/coaching/index.xml new file mode 100644 index 000000000..73071856f --- /dev/null +++ b/public/tags/coaching/index.xml @@ -0,0 +1,25 @@ + + + + coaching on Canadian Data Guy + https://canadiandataguy.com/tags/coaching/ + Recent content in coaching on Canadian Data Guy + Hugo -- gohugo.io + en-us + + + 1 on 1 Interview Coaching + https://canadiandataguy.com/blog/1-on-1-coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/1-on-1-coaching/ + Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks &amp; Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + + Youtube + https://canadiandataguy.com/blog/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/youtube/ + + + + diff --git a/public/tags/coaching/page/1/index.html b/public/tags/coaching/page/1/index.html new file mode 100644 index 000000000..d7472066e --- /dev/null +++ b/public/tags/coaching/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/coaching/ + + + + + + diff --git a/public/tags/concurrency/index.html b/public/tags/concurrency/index.html new file mode 100644 index 000000000..045eb0df3 --- /dev/null +++ b/public/tags/concurrency/index.html @@ -0,0 +1,765 @@ + + + + + + + + + +concurrency + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    concurrency

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Solving Delta Table Concurrency Issues

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + September 30, 2023 +

    + +
    + +

    Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/concurrency/index.xml b/public/tags/concurrency/index.xml new file mode 100644 index 000000000..df779d568 --- /dev/null +++ b/public/tags/concurrency/index.xml @@ -0,0 +1,19 @@ + + + + concurrency on Canadian Data Guy + https://canadiandataguy.com/tags/concurrency/ + Recent content in concurrency on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + diff --git a/public/tags/concurrency/page/1/index.html b/public/tags/concurrency/page/1/index.html new file mode 100644 index 000000000..350e519fb --- /dev/null +++ b/public/tags/concurrency/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/concurrency/ + + + + + + diff --git a/public/tags/cost-savings/index.html b/public/tags/cost-savings/index.html new file mode 100644 index 000000000..ba8d27598 --- /dev/null +++ b/public/tags/cost-savings/index.html @@ -0,0 +1,830 @@ + + + + + + + + + +cost-savings + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    cost-savings

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    A Productive Life: How to Parallelize Code Execution in Python

    +
    +

    + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 23, 2023 +

    + +
    + +

    A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How to Cut Your Data Processing Costs by 30% with Graviton

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 23, 2023 +

    + +
    + +

    How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/cost-savings/index.xml b/public/tags/cost-savings/index.xml new file mode 100644 index 000000000..5b729774f --- /dev/null +++ b/public/tags/cost-savings/index.xml @@ -0,0 +1,27 @@ + + + + cost-savings on Canadian Data Guy + https://canadiandataguy.com/tags/cost-savings/ + Recent content in cost-savings on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sun, 23 Apr 2023 21:38:16 +0000 + + + A Productive Life: How to Parallelize Code Execution in Python + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + diff --git a/public/tags/cost-savings/page/1/index.html b/public/tags/cost-savings/page/1/index.html new file mode 100644 index 000000000..3d7e9fc83 --- /dev/null +++ b/public/tags/cost-savings/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/cost-savings/ + + + + + + diff --git a/public/tags/dbsql/index.html b/public/tags/dbsql/index.html new file mode 100644 index 000000000..b25ad2c84 --- /dev/null +++ b/public/tags/dbsql/index.html @@ -0,0 +1,766 @@ + + + + + + + + + +dbsql + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    dbsql

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + September 15, 2023 +

    + +
    + +

    Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You’re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/dbsql/index.xml b/public/tags/dbsql/index.xml new file mode 100644 index 000000000..073b46c9e --- /dev/null +++ b/public/tags/dbsql/index.xml @@ -0,0 +1,20 @@ + + + + dbsql on Canadian Data Guy + https://canadiandataguy.com/tags/dbsql/ + Recent content in dbsql on Canadian Data Guy + Hugo -- gohugo.io + en-us + Fri, 15 Sep 2023 17:29:06 +0000 + + + Databricks SQL Dashboards Guide: Tips and Tricks to Master Thems + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-15-databricks-sql-dashboards-guide-tips-and-tricks-to-master-them/ + Databricks SQL Dashboards Guide: Tips and Tricks to Master Them Welcome to the world of Databricks SQL Dashboards! You&rsquo;re in the right place if you want to learn how to go beyond just building visualizations and add some tricks to your arsenal. This guide will walk you through creating, managing, and optimizing your Databricks SQL dashboards. +1. Getting Started with Viewing and Organizing Dashboards: Accessing Your Dashboards: Navigate to the workspace browser and click “Workspace” in the sidebar. + + + diff --git a/public/tags/dbsql/page/1/index.html b/public/tags/dbsql/page/1/index.html new file mode 100644 index 000000000..aee1f51a8 --- /dev/null +++ b/public/tags/dbsql/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/dbsql/ + + + + + + diff --git a/public/tags/delta-live-tables/index.html b/public/tags/delta-live-tables/index.html new file mode 100644 index 000000000..04037cd7b --- /dev/null +++ b/public/tags/delta-live-tables/index.html @@ -0,0 +1,980 @@ + + + + + + + + + +delta live tables + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    delta live tables

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Delta Live Tables Advanced Q & A

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 3, 2023 +

    + +
    + +

    Delta Live Tables Advanced Q & A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How to upgrade your Spark Stream application with a new checkpoint!

    +
    +

    + + + + in + + streaming, + spark streaming + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + January 25, 2023 +

    + +
    + +

    How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How to parameterize Delta Live Tables and import reusable functions

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + December 13, 2022 +

    + +
    + +

    How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

    +
    +

    + + + + in + + customer stories, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 5, 2022 +

    + +
    + +

    To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/delta-live-tables/index.xml b/public/tags/delta-live-tables/index.xml new file mode 100644 index 000000000..0b2fc6f6b --- /dev/null +++ b/public/tags/delta-live-tables/index.xml @@ -0,0 +1,46 @@ + + + + delta live tables on Canadian Data Guy + https://canadiandataguy.com/tags/delta-live-tables/ + Recent content in delta live tables on Canadian Data Guy + Hugo -- gohugo.io + en-us + Fri, 03 Mar 2023 17:35:21 -0500 + + + Delta Live Tables Advanced Q & A + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/deltalivetablesadvancedqa/ + Delta Live Tables Advanced Q &amp; A This is primarily written for those trying to handle edge cases. +Q1.) How can a single/unified table be built with historical backfill and ongoing streaming Kafka data? The streaming table built using DLT allows writes to the table outside of the DLT. Thus, you can build and run your DLT pipeline with Kafka as a source, generating the physical table with a name. Then, you can do a streaming write to this table outside DLT. + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + How to parameterize Delta Live Tables and import reusable functions + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + Tue, 13 Dec 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoparameterizedeltalivetablesandimportreusablefunctions/ + How to parameterize Delta Live Tables and import reusable functions with working code This blog will discuss passing custom parameters to a Delta Live Tables (DLT) pipeline. Furthermore, we will discuss importing functions defined in other files or locations. You can import files from the current directory or a specified location using sys.path.append(). +Update: As of *December 2022, you can directly import files if the reusable_functions.py file exists in the same repository by just using the import command, which is the preferred approach. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/tags/delta-live-tables/page/1/index.html b/public/tags/delta-live-tables/page/1/index.html new file mode 100644 index 000000000..61932fed5 --- /dev/null +++ b/public/tags/delta-live-tables/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/delta-live-tables/ + + + + + + diff --git a/public/tags/delta/index.html b/public/tags/delta/index.html new file mode 100644 index 000000000..ed96cebb2 --- /dev/null +++ b/public/tags/delta/index.html @@ -0,0 +1,1111 @@ + + + + + + + + + +delta + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    delta

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Solving Delta Table Concurrency Issues

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + September 30, 2023 +

    + +
    + +

    Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + September 12, 2023 +

    + +
    + +

    Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial?

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + June 6, 2023 +

    + +
    + +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users

    +
    +

    + + + + in + + best practices, + spark streaming + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 19, 2023 +

    + +
    + +

    Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

    +
    +

    + + + + in + + customer stories + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 24, 2022 +

    + +
    + +

    ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

    +
    +

    + + + + in + + customer stories, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 5, 2022 +

    + +
    + +

    To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/delta/index.xml b/public/tags/delta/index.xml new file mode 100644 index 000000000..e240c7972 --- /dev/null +++ b/public/tags/delta/index.xml @@ -0,0 +1,56 @@ + + + + delta on Canadian Data Guy + https://canadiandataguy.com/tags/delta/ + Recent content in delta on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + Solving Delta Table Concurrency Issues + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-29-solvingdeltatableconcurrencyissuespracticalcodesolutionsinsights/ + Solving Delta Table Concurrency Issues Delta Lake is a powerful technology for bringing ACID transactions to your data lakes. It allows multiple operations to be performed on a dataset concurrently. However, dealing with concurrent operations can sometimes be tricky and may lead to issues such as ConcurrentAppendException, ConcurrentDeleteReadException, and ConcurrentDeleteDeleteException. In this blog post, we will explore why these issues occur and how to handle them effectively using a Python function, and how to avoid them with table design and using isolation levels and write conflicts. + + + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Tue, 12 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-09-12-optimizing-databricks-sql-achieving-blazing-fast-query-speeds-at-scale/ + Optimizing Databricks SQL: Achieving Blazing-Fast Query Speeds at Scale In this data age, delivering a seamless user experience is paramount. While there are numerous ways to measure this experience, one metric stands tall when evaluating the responsiveness of applications and databases: the P99 latency. Especially vital for SQL queries, this seemingly esoteric number is, in reality, a powerful gauge of the experience we provide to our customers. Why is it so crucial? + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Wed, 19 Apr 2023 01:04:45 +0000 + https://canadiandataguy.com/blog/2023-04-18-spark-streaming-best-practices-a-bare-minimum-checklist-for-beginners-and-advanced-users/ + Spark Streaming Best Practices-A bare minimum checklist for Beginners and Advanced Users Most good things in life come with a nuance. While learning Streaming a few years ago, I spent hours searching for best practices. However, I would find answers to be complicated to make sense for a beginner’s mind. Thus, I devised a set of best practices that should hold true in almost all scenarios. +The below checklist is not ordered, you should aim to check off as many items as you can. + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/tags/delta/page/1/index.html b/public/tags/delta/page/1/index.html new file mode 100644 index 000000000..b1c1f1388 --- /dev/null +++ b/public/tags/delta/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/delta/ + + + + + + diff --git a/public/tags/foreachbatch/index.html b/public/tags/foreachbatch/index.html new file mode 100644 index 000000000..d684d8084 --- /dev/null +++ b/public/tags/foreachbatch/index.html @@ -0,0 +1,910 @@ + + + + + + + + + +foreachbatch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    foreachbatch

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + June 6, 2023 +

    + +
    + +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Merge Multiple Spark Streams Into A Delta Table

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 13, 2022 +

    + +
    + +

    Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 12, 2022 +

    + +
    + +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/foreachbatch/index.xml b/public/tags/foreachbatch/index.xml new file mode 100644 index 000000000..b98b254e0 --- /dev/null +++ b/public/tags/foreachbatch/index.xml @@ -0,0 +1,38 @@ + + + + foreachbatch on Canadian Data Guy + https://canadiandataguy.com/tags/foreachbatch/ + Recent content in foreachbatch on Canadian Data Guy + Hugo -- gohugo.io + en-us + Tue, 06 Jun 2023 17:29:06 +0000 + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/tags/foreachbatch/page/1/index.html b/public/tags/foreachbatch/page/1/index.html new file mode 100644 index 000000000..f11d75970 --- /dev/null +++ b/public/tags/foreachbatch/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/foreachbatch/ + + + + + + diff --git a/public/tags/graviton/index.html b/public/tags/graviton/index.html new file mode 100644 index 000000000..911e0b08b --- /dev/null +++ b/public/tags/graviton/index.html @@ -0,0 +1,765 @@ + + + + + + + + + +graviton + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    graviton

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to Cut Your Data Processing Costs by 30% with Graviton

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 23, 2023 +

    + +
    + +

    How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/graviton/index.xml b/public/tags/graviton/index.xml new file mode 100644 index 000000000..80db43de3 --- /dev/null +++ b/public/tags/graviton/index.xml @@ -0,0 +1,19 @@ + + + + graviton on Canadian Data Guy + https://canadiandataguy.com/tags/graviton/ + Recent content in graviton on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sun, 23 Apr 2023 17:29:06 +0000 + + + How to Cut Your Data Processing Costs by 30% with Graviton + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-04-23-how-to-cut-your-data-processing-costs-by-30-with-graviton/ + How to Cut Your Data Processing Costs by 30% with Graviton What is AWS Graviton ? AWS Graviton is a family of Arm-based processors that are designed by AWS to provide cost-effective and high-performance computing for cloud workloads. Graviton processors are built using 64-bit Arm, which are optimized for power efficiency and performance. They offer a more cost-effective alternative to traditional x86-based processors, making them a popular choice for running a variety of workloads on AWS. + + + diff --git a/public/tags/graviton/page/1/index.html b/public/tags/graviton/page/1/index.html new file mode 100644 index 000000000..170cd402d --- /dev/null +++ b/public/tags/graviton/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/graviton/ + + + + + + diff --git a/public/tags/index.html b/public/tags/index.html new file mode 100644 index 000000000..8a6ec2823 --- /dev/null +++ b/public/tags/index.html @@ -0,0 +1,656 @@ + + + + + + + + + +Tags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    Tags

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/index.xml b/public/tags/index.xml new file mode 100644 index 000000000..ab59df924 --- /dev/null +++ b/public/tags/index.xml @@ -0,0 +1,180 @@ + + + + Tags on Canadian Data Guy + https://canadiandataguy.com/tags/ + Recent content in Tags on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sat, 30 Sep 2023 17:29:06 +0000 + + + concurrency + https://canadiandataguy.com/tags/concurrency/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/concurrency/ + + + + delta + https://canadiandataguy.com/tags/delta/ + Sat, 30 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/delta/ + + + + dbsql + https://canadiandataguy.com/tags/dbsql/ + Fri, 15 Sep 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/dbsql/ + + + + foreachbatch + https://canadiandataguy.com/tags/foreachbatch/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/foreachbatch/ + + + + streaming + https://canadiandataguy.com/tags/streaming/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/streaming/ + + + + cost-savings + https://canadiandataguy.com/tags/cost-savings/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/tags/cost-savings/ + + + + parallelization + https://canadiandataguy.com/tags/parallelization/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/tags/parallelization/ + + + + python + https://canadiandataguy.com/tags/python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/tags/python/ + + + + graviton + https://canadiandataguy.com/tags/graviton/ + Sun, 23 Apr 2023 17:29:06 +0000 + https://canadiandataguy.com/tags/graviton/ + + + + checkpoint + https://canadiandataguy.com/tags/checkpoint/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/tags/checkpoint/ + + + + spark streaming + https://canadiandataguy.com/tags/spark-streaming/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/tags/spark-streaming/ + + + + delta live tables + https://canadiandataguy.com/tags/delta-live-tables/ + Fri, 03 Mar 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/delta-live-tables/ + + + + job_id + https://canadiandataguy.com/tags/job_id/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/job_id/ + + + + run_id + https://canadiandataguy.com/tags/run_id/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/run_id/ + + + + workspace + https://canadiandataguy.com/tags/workspace/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/workspace/ + + + + books + https://canadiandataguy.com/tags/books/ + Fri, 27 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/books/ + + + + kafka + https://canadiandataguy.com/tags/kafka/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/tags/kafka/ + + + + merge + https://canadiandataguy.com/tags/merge/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/tags/merge/ + + + + optimize + https://canadiandataguy.com/tags/optimize/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/tags/optimize/ + + + + z order + https://canadiandataguy.com/tags/z-order/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/tags/z-order/ + + + + spark + https://canadiandataguy.com/tags/spark/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/tags/spark/ + + + + coaching + https://canadiandataguy.com/tags/coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/tags/coaching/ + + + + interviewing + https://canadiandataguy.com/tags/interviewing/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/tags/interviewing/ + + + + youtube + https://canadiandataguy.com/tags/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/tags/youtube/ + + + + diff --git a/public/tags/interviewing/index.html b/public/tags/interviewing/index.html new file mode 100644 index 000000000..77fa52ca9 --- /dev/null +++ b/public/tags/interviewing/index.html @@ -0,0 +1,732 @@ + + + + + + + + + +interviewing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    interviewing

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    1 on 1 Interview Coaching

    +
    +

    + + + + in + + coaching + + + +

    + +
    + +

    Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks & Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/interviewing/index.xml b/public/tags/interviewing/index.xml new file mode 100644 index 000000000..f6d12d98c --- /dev/null +++ b/public/tags/interviewing/index.xml @@ -0,0 +1,18 @@ + + + + interviewing on Canadian Data Guy + https://canadiandataguy.com/tags/interviewing/ + Recent content in interviewing on Canadian Data Guy + Hugo -- gohugo.io + en-us + + + 1 on 1 Interview Coaching + https://canadiandataguy.com/blog/1-on-1-coaching/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/1-on-1-coaching/ + Welcome! I am a Super Coach . I have worked at some of the biggest tech companies, including Databricks &amp; Amazon. I make great efforts to use my platform to share resources and show folks the path of growth that exists without taking the management ladder track while inspiring many immigrants and people of colour to understand and realize their true market potential. To further my mission, I help folks negotiate job offers, $2+ Million negotiated so far. + + + diff --git a/public/tags/interviewing/page/1/index.html b/public/tags/interviewing/page/1/index.html new file mode 100644 index 000000000..1b522911e --- /dev/null +++ b/public/tags/interviewing/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/interviewing/ + + + + + + diff --git a/public/tags/job_id/index.html b/public/tags/job_id/index.html new file mode 100644 index 000000000..ee4414e8d --- /dev/null +++ b/public/tags/job_id/index.html @@ -0,0 +1,768 @@ + + + + + + + + + +job_id + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    job_id

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to get the Job ID and Run ID for a Databricks Job

    +
    +

    + + + + in + + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + February 23, 2023 +

    + +
    + +

    How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f""" job_id: {dbutils.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/job_id/index.xml b/public/tags/job_id/index.xml new file mode 100644 index 000000000..3d49b678e --- /dev/null +++ b/public/tags/job_id/index.xml @@ -0,0 +1,23 @@ + + + + job_id on Canadian Data Guy + https://canadiandataguy.com/tags/job_id/ + Recent content in job_id on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Feb 2023 17:35:21 -0500 + + + How to get the Job ID and Run ID for a Databricks Job + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f&quot;&quot;&quot; job_id: {dbutils. + + + diff --git a/public/tags/job_id/page/1/index.html b/public/tags/job_id/page/1/index.html new file mode 100644 index 000000000..893d04284 --- /dev/null +++ b/public/tags/job_id/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/job_id/ + + + + + + diff --git a/public/tags/kafka/index.html b/public/tags/kafka/index.html new file mode 100644 index 000000000..0030954bf --- /dev/null +++ b/public/tags/kafka/index.html @@ -0,0 +1,840 @@ + + + + + + + + + +kafka + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    kafka

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to upgrade your Spark Stream application with a new checkpoint!

    +
    +

    + + + + in + + streaming, + spark streaming + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + January 25, 2023 +

    + +
    + +

    How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 12, 2022 +

    + +
    + +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/kafka/index.xml b/public/tags/kafka/index.xml new file mode 100644 index 000000000..d87281dde --- /dev/null +++ b/public/tags/kafka/index.xml @@ -0,0 +1,31 @@ + + + + kafka on Canadian Data Guy + https://canadiandataguy.com/tags/kafka/ + Recent content in kafka on Canadian Data Guy + Hugo -- gohugo.io + en-us + Wed, 25 Jan 2023 17:35:21 -0500 + + + How to upgrade your Spark Stream application with a new checkpoint! + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtoupgradeyoursparkstreamapplicationwithanewcheckpoint/ + How to upgrade your Spark Stream application with a new checkpoint With working code Sometimes in life, we need to make breaking changes which require us to create a new checkpoint. Some example scenarios: +You are doing a code/application change where you are changing logic +Major Spark Version upgrade from Spark 2.x to Spark 3.x +The previous deployment was wrong, and you want to reprocess from a certain point +There could be plenty of scenarios where you want to control precisely which data(Kafka offsets) need to be processed. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/tags/kafka/page/1/index.html b/public/tags/kafka/page/1/index.html new file mode 100644 index 000000000..e7030e046 --- /dev/null +++ b/public/tags/kafka/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/kafka/ + + + + + + diff --git a/public/tags/merge/index.html b/public/tags/merge/index.html new file mode 100644 index 000000000..02616670d --- /dev/null +++ b/public/tags/merge/index.html @@ -0,0 +1,840 @@ + + + + + + + + + +merge + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    merge

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Merge Multiple Spark Streams Into A Delta Table

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 13, 2022 +

    + +
    + +

    Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 12, 2022 +

    + +
    + +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/merge/index.xml b/public/tags/merge/index.xml new file mode 100644 index 000000000..cf65206ba --- /dev/null +++ b/public/tags/merge/index.xml @@ -0,0 +1,30 @@ + + + + merge on Canadian Data Guy + https://canadiandataguy.com/tags/merge/ + Recent content in merge on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 13 Oct 2022 04:09:03 -0500 + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/tags/merge/page/1/index.html b/public/tags/merge/page/1/index.html new file mode 100644 index 000000000..701ab37f8 --- /dev/null +++ b/public/tags/merge/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/merge/ + + + + + + diff --git a/public/tags/optimize/index.html b/public/tags/optimize/index.html new file mode 100644 index 000000000..40b9575e9 --- /dev/null +++ b/public/tags/optimize/index.html @@ -0,0 +1,977 @@ + + + + + + + + + +optimize + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    optimize

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Merge Multiple Spark Streams Into A Delta Table

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 13, 2022 +

    + +
    + +

    Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 12, 2022 +

    + +
    + +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

    +
    +

    + + + + in + + customer stories + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 24, 2022 +

    + +
    + +

    ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

    +
    +

    + + + + in + + customer stories, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 5, 2022 +

    + +
    + +

    To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/optimize/index.xml b/public/tags/optimize/index.xml new file mode 100644 index 000000000..cd55e07b7 --- /dev/null +++ b/public/tags/optimize/index.xml @@ -0,0 +1,44 @@ + + + + optimize on Canadian Data Guy + https://canadiandataguy.com/tags/optimize/ + Recent content in optimize on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 13 Oct 2022 04:09:03 -0500 + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/tags/optimize/page/1/index.html b/public/tags/optimize/page/1/index.html new file mode 100644 index 000000000..10bc2f7bc --- /dev/null +++ b/public/tags/optimize/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/optimize/ + + + + + + diff --git a/public/tags/page/1/index.html b/public/tags/page/1/index.html new file mode 100644 index 000000000..94babb69f --- /dev/null +++ b/public/tags/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/ + + + + + + diff --git a/public/tags/parallelization/index.html b/public/tags/parallelization/index.html new file mode 100644 index 000000000..3b36dda23 --- /dev/null +++ b/public/tags/parallelization/index.html @@ -0,0 +1,761 @@ + + + + + + + + + +parallelization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    parallelization

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    A Productive Life: How to Parallelize Code Execution in Python

    +
    +

    + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 23, 2023 +

    + +
    + +

    A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/parallelization/index.xml b/public/tags/parallelization/index.xml new file mode 100644 index 000000000..b12604e51 --- /dev/null +++ b/public/tags/parallelization/index.xml @@ -0,0 +1,20 @@ + + + + parallelization on Canadian Data Guy + https://canadiandataguy.com/tags/parallelization/ + Recent content in parallelization on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sun, 23 Apr 2023 21:38:16 +0000 + + + A Productive Life: How to Parallelize Code Execution in Python + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. + + + diff --git a/public/tags/parallelization/page/1/index.html b/public/tags/parallelization/page/1/index.html new file mode 100644 index 000000000..d221f3a04 --- /dev/null +++ b/public/tags/parallelization/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/parallelization/ + + + + + + diff --git a/public/tags/python/index.html b/public/tags/python/index.html new file mode 100644 index 000000000..da890485a --- /dev/null +++ b/public/tags/python/index.html @@ -0,0 +1,761 @@ + + + + + + + + + +python + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    python

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    A Productive Life: How to Parallelize Code Execution in Python

    +
    +

    + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + April 23, 2023 +

    + +
    + +

    A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/python/index.xml b/public/tags/python/index.xml new file mode 100644 index 000000000..68a91f7c3 --- /dev/null +++ b/public/tags/python/index.xml @@ -0,0 +1,20 @@ + + + + python on Canadian Data Guy + https://canadiandataguy.com/tags/python/ + Recent content in python on Canadian Data Guy + Hugo -- gohugo.io + en-us + Sun, 23 Apr 2023 21:38:16 +0000 + + + A Productive Life: How to Parallelize Code Execution in Python + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + Sun, 23 Apr 2023 21:38:16 +0000 + https://canadiandataguy.com/blog/2023-04-23-a-productive-life-how-to-parallelize-code-execution-in-python/ + A Productive Life: How to Parallelize Code Execution in Python Asynchronous programming has become increasingly popular in recent years, especially in web development, where it is used to build high-performance, scalable applications. Python has built-in support for asynchronous programming through the asyncio module, which provides a powerful framework for writing asynchronous code. +In this blog post, we will explore the asyncio module in Python 3.10 and learn how to run tasks in parallel using the new features introduced in this version. + + + diff --git a/public/tags/python/page/1/index.html b/public/tags/python/page/1/index.html new file mode 100644 index 000000000..70b905534 --- /dev/null +++ b/public/tags/python/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/python/ + + + + + + diff --git a/public/tags/run_id/index.html b/public/tags/run_id/index.html new file mode 100644 index 000000000..c45fafb5d --- /dev/null +++ b/public/tags/run_id/index.html @@ -0,0 +1,768 @@ + + + + + + + + + +run_id + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    run_id

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to get the Job ID and Run ID for a Databricks Job

    +
    +

    + + + + in + + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + February 23, 2023 +

    + +
    + +

    How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f""" job_id: {dbutils.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/run_id/index.xml b/public/tags/run_id/index.xml new file mode 100644 index 000000000..77c5860ac --- /dev/null +++ b/public/tags/run_id/index.xml @@ -0,0 +1,23 @@ + + + + run_id on Canadian Data Guy + https://canadiandataguy.com/tags/run_id/ + Recent content in run_id on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Feb 2023 17:35:21 -0500 + + + How to get the Job ID and Run ID for a Databricks Job + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howtogetthejobidandrunidforadatabricksjob/ + How to get the Job ID and Run ID for a Databricks Job with working code Sometimes there is a need to store or print system-generated values like job_id, run_id, start_time, etc. These entities are called ‘task parameter variables’. A list of supported parameters is listed here. +This is a simple 2-step process: +Pass the parameter when defining the job/task +Get/Fetch and print the values +Step 1: Pass the parameters Step 2: Get/Fetch and print the values print(f&quot;&quot;&quot; job_id: {dbutils. + + + diff --git a/public/tags/run_id/page/1/index.html b/public/tags/run_id/page/1/index.html new file mode 100644 index 000000000..13b6e71f1 --- /dev/null +++ b/public/tags/run_id/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/run_id/ + + + + + + diff --git a/public/tags/spark-streaming/index.html b/public/tags/spark-streaming/index.html new file mode 100644 index 000000000..dcf4d140e --- /dev/null +++ b/public/tags/spark-streaming/index.html @@ -0,0 +1,974 @@ + + + + + + + + + +spark streaming + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    spark streaming

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    How to write your first Spark application with Stream-Stream Joins with working code.

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 23, 2023 +

    + +
    + +

    How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Dive Deep into Spark Streaming Checkpoint

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 21, 2023 +

    + +
    + +

    From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

    +
    +

    + + + + in + + customer stories + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 24, 2022 +

    + +
    + +

    ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

    +
    +

    + + + + in + + customer stories, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 5, 2022 +

    + +
    + +

    To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/spark-streaming/index.xml b/public/tags/spark-streaming/index.xml new file mode 100644 index 000000000..21b917a1e --- /dev/null +++ b/public/tags/spark-streaming/index.xml @@ -0,0 +1,43 @@ + + + + spark streaming on Canadian Data Guy + https://canadiandataguy.com/tags/spark-streaming/ + Recent content in spark streaming on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Mar 2023 06:32:42 +0000 + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/tags/spark-streaming/page/1/index.html b/public/tags/spark-streaming/page/1/index.html new file mode 100644 index 000000000..f8e86d5de --- /dev/null +++ b/public/tags/spark-streaming/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/spark-streaming/ + + + + + + diff --git a/public/tags/spark/index.html b/public/tags/spark/index.html new file mode 100644 index 000000000..dbeacdf6e --- /dev/null +++ b/public/tags/spark/index.html @@ -0,0 +1,833 @@ + + + + + + + + + +spark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    spark

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions

    +
    +

    + + + + in + + customer stories + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 24, 2022 +

    + +
    + +

    ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments

    +
    +

    + + + + in + + customer stories, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + May 5, 2022 +

    + +
    + +

    To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/spark/index.xml b/public/tags/spark/index.xml new file mode 100644 index 000000000..c83c7a3ff --- /dev/null +++ b/public/tags/spark/index.xml @@ -0,0 +1,26 @@ + + + + spark on Canadian Data Guy + https://canadiandataguy.com/tags/spark/ + Recent content in spark on Canadian Data Guy + Hugo -- gohugo.io + en-us + Tue, 24 May 2022 17:35:21 -0500 + + + ARC Uses a Lakehouse Architecture for Real-time Data Insights That Optimize Drilling Performance and Lower Carbon Emissions + https://canadiandataguy.com/blog/arcresources/ + Tue, 24 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/arcresources/ + ARC has deployed the Databricks Lakehouse Platform to enable its drilling engineers to monitor operational metrics in near real-time, so that we can proactively identify any potential issues and enable agile mitigation measures. In addition to improving drilling precision, this solution has helped us in reducing drilling time for one of our fields. Time saving translates to reduction in fuel used and therefore a reduction in CO2 footprint that result from drilling operations. + + + How Audantic Uses Databricks Delta Live Tables to Increase Productivity for Real Estate Market Segments + https://canadiandataguy.com/blog/audantic/ + Thu, 05 May 2022 17:35:21 -0500 + https://canadiandataguy.com/blog/audantic/ + To support our data-driven initiatives, we had ‘stitched’ together various services for ETL, orchestration, ML leveraging AWS, Airflow, where we saw some success but quickly turned into an overly complex system that took nearly five times as long to develop compared to the new solution. Our team captured high-level metrics comparing our previous implementation and current lakehouse solution. As you can see from the table below, we spent months developing our previous solution and had to write approximately 3 times as much code. + + + diff --git a/public/tags/spark/page/1/index.html b/public/tags/spark/page/1/index.html new file mode 100644 index 000000000..e63513156 --- /dev/null +++ b/public/tags/spark/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/spark/ + + + + + + diff --git a/public/tags/streaming/index.html b/public/tags/streaming/index.html new file mode 100644 index 000000000..2418a64fc --- /dev/null +++ b/public/tags/streaming/index.html @@ -0,0 +1,978 @@ + + + + + + + + + +streaming + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    streaming

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code

    +
    +

    + + + + in + + databricks, + spark + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + June 6, 2023 +

    + +
    + +

    Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming & foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How to write your first Spark application with Stream-Stream Joins with working code.

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 23, 2023 +

    + +
    + +

    How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join?

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Dive Deep into Spark Streaming Checkpoint

    +
    +

    + + + + in + + streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + March 21, 2023 +

    + +
    + +

    From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    How I wrote my first Spark Streaming Application with Joins?

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + January 25, 2023 +

    + +
    + +

    How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/streaming/index.xml b/public/tags/streaming/index.xml new file mode 100644 index 000000000..7984d0ea4 --- /dev/null +++ b/public/tags/streaming/index.xml @@ -0,0 +1,45 @@ + + + + streaming on Canadian Data Guy + https://canadiandataguy.com/tags/streaming/ + Recent content in streaming on Canadian Data Guy + Hugo -- gohugo.io + en-us + Tue, 06 Jun 2023 17:29:06 +0000 + + + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Tue, 06 Jun 2023 17:29:06 +0000 + https://canadiandataguy.com/blog/2023-06-06-simplifying-real-time-data-processing-with-spark-streamings-foreachbatch-with-working-code/ + Simplifying Real-time Data Processing with Spark Streaming’s foreachBatch with working code Comprehensive guide to implementing a fully operational Streaming Pipeline that can be tailored to your specific needs. In this working example, you will learn how to parameterize the ForEachBatch function. +Spark Streaming &amp; foreachBatch Spark Streaming is a powerful tool for processing streaming data. It allows you to process data as it arrives, without having to wait for the entire dataset to be available. + + + How to write your first Spark application with Stream-Stream Joins with working code. + https://canadiandataguy.com/blog/spark-stream-stream-join/ + Thu, 23 Mar 2023 06:32:42 +0000 + https://canadiandataguy.com/blog/spark-stream-stream-join/ + How to write your first Spark application with Stream-Stream Joins with working code. Have you been waiting to try Streaming but cannot take the plunge? +In a single blog, we will teach you whatever needs to be understood about Streaming Joins. We will give you a working code which you can use for your next Streaming Pipeline. +The steps involved: +Create a fake dataset at scale Set a baseline using traditional SQL Define Temporary Streaming Views Inner Joins with optional Watermarking Left Joins with Watermarking The cold start edge case: withEventTimeOrder Cleanup What is Stream-Stream Join? + + + Dive Deep into Spark Streaming Checkpoint + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + Tue, 21 Mar 2023 06:14:44 +0000 + https://canadiandataguy.com/blog/frombeginnertoproacomprehensiveguidetounderstandingthesparkstreamingcheckpoint/ + From Beginner to Pro: A Comprehensive Guide to understanding the Spark Streaming Checkpoint Spark is a distributed computing framework that allows for processing large datasets in parallel across a cluster of computers. When running a Spark job, it is not uncommon to encounter failures due to various issues such as network or hardware failures, software bugs, or even insufficient memory. One way to address these issues is to re-run the entire job from the beginning, which can be time-consuming and inefficient. + + + How I wrote my first Spark Streaming Application with Joins? + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + Wed, 25 Jan 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/howiwrotemyfirstsparkstreamingapplicationwithjoins/ + How I wrote my first Spark Streaming Application with Joins with working code When I started learning about Spark Streaming, I could not find enough code/material which could kick-start my journey and build my confidence. I wrote this blog to fill this gap which could help beginners understand how simple streaming is and build their first application. +In this blog, I will explain most things by first principles to increase your understanding and confidence and you walk away with code for your first Streaming application. + + + diff --git a/public/tags/streaming/page/1/index.html b/public/tags/streaming/page/1/index.html new file mode 100644 index 000000000..d756c3e6b --- /dev/null +++ b/public/tags/streaming/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/streaming/ + + + + + + diff --git a/public/tags/workspace/index.html b/public/tags/workspace/index.html new file mode 100644 index 000000000..be5e678e3 --- /dev/null +++ b/public/tags/workspace/index.html @@ -0,0 +1,766 @@ + + + + + + + + + +workspace + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    workspace

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users

    +
    +

    + + + + in + + best practices, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + February 23, 2023 +

    + +
    + +

    Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/workspace/index.xml b/public/tags/workspace/index.xml new file mode 100644 index 000000000..582cb9628 --- /dev/null +++ b/public/tags/workspace/index.xml @@ -0,0 +1,20 @@ + + + + workspace on Canadian Data Guy + https://canadiandataguy.com/tags/workspace/ + Recent content in workspace on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 23 Feb 2023 17:35:21 -0500 + + + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Thu, 23 Feb 2023 17:35:21 -0500 + https://canadiandataguy.com/blog/databricksworkspacebestpracticesachecklistforbothbeginnersandadvancedusers/ + Databricks Workspace Best Practices- A checklist for both beginners and Advanced Users Most good things in life come with a nuance. While learning Databricks a few years ago, I spent hours searching for best practices. Thus, I devised a set of best rules that should hold in almost all scenarios. These will help you start on the right foot. +Here are some basic rules for using Databricks Workspace: Version control everything: Use Repos and organize your notebooks and folders: Keep your notebooks and files in folders to make them easy to find and manage. + + + diff --git a/public/tags/workspace/page/1/index.html b/public/tags/workspace/page/1/index.html new file mode 100644 index 000000000..e7f203b37 --- /dev/null +++ b/public/tags/workspace/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/workspace/ + + + + + + diff --git a/public/tags/youtube/index.html b/public/tags/youtube/index.html new file mode 100644 index 000000000..e7935188c --- /dev/null +++ b/public/tags/youtube/index.html @@ -0,0 +1,732 @@ + + + + + + + + + +youtube + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    youtube

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Youtube

    +
    +

    + + + + in + + coaching + + + +

    + +
    + +

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/youtube/index.xml b/public/tags/youtube/index.xml new file mode 100644 index 000000000..fc7e3fdf2 --- /dev/null +++ b/public/tags/youtube/index.xml @@ -0,0 +1,18 @@ + + + + youtube on Canadian Data Guy + https://canadiandataguy.com/tags/youtube/ + Recent content in youtube on Canadian Data Guy + Hugo -- gohugo.io + en-us + + + Youtube + https://canadiandataguy.com/blog/youtube/ + Mon, 01 Jan 0001 00:00:00 +0000 + https://canadiandataguy.com/blog/youtube/ + + + + diff --git a/public/tags/youtube/page/1/index.html b/public/tags/youtube/page/1/index.html new file mode 100644 index 000000000..68a151d94 --- /dev/null +++ b/public/tags/youtube/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/youtube/ + + + + + + diff --git a/public/tags/z-order/index.html b/public/tags/z-order/index.html new file mode 100644 index 000000000..f04f85016 --- /dev/null +++ b/public/tags/z-order/index.html @@ -0,0 +1,840 @@ + + + + + + + + + +z order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    +
    +
    +

    z order

    +
    +
    +
    +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +
    +

    Merge Multiple Spark Streams Into A Delta Table

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 13, 2022 +

    + +
    + +

    Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing.

    +

    Continue reading +

    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code

    +
    +

    + + + + in + + streaming, + spark streaming, + databricks + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + October 12, 2022 +

    + +
    + +

    Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch.

    +

    Continue reading +

    + +
    +
    +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    + +
    + + +
    +
    + + +
    +

    About us

    + +

    We offer expertise and consulting in data engineering, analytics and cloud computing.

    + + + +
    + + + +
    + + + +
    + + + +
    + +

    Contact

    + +

    Canadian Data Guy Corp. +
    Calgary, Canada +

    + + + Go to contact page + + + +
    + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tags/z-order/index.xml b/public/tags/z-order/index.xml new file mode 100644 index 000000000..1dc2bcd82 --- /dev/null +++ b/public/tags/z-order/index.xml @@ -0,0 +1,30 @@ + + + + z order on Canadian Data Guy + https://canadiandataguy.com/tags/z-order/ + Recent content in z order on Canadian Data Guy + Hugo -- gohugo.io + en-us + Thu, 13 Oct 2022 04:09:03 -0500 + + + Merge Multiple Spark Streams Into A Delta Table + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Thu, 13 Oct 2022 04:09:03 -0500 + https://canadiandataguy.com/blog/mergemultiplesparkstreamsintoadeltatable/ + Merge Multiple Spark Streams Into A Delta Table with working code This blog will discuss how to read from multiple Spark Streams and merge/upsert data into a single Delta Table. We will also optimize/cluster data of the delta table. +Overall, the process works in the following manner: +Read data from a streaming source +Use this special function ***foreachBatch. ***Using this we will call any user-defined function responsible for all the processing. + + + Using Spark Streaming to merge/upsert data into a Delta Lake with working code + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Wed, 12 Oct 2022 04:06:14 -0500 + https://canadiandataguy.com/blog/usingsparkstreamingtomergeupsertdataintoadeltalakewithworkingcode/ + Using Spark Streaming to merge/upsert data into a Delta Lake with working code This blog will discuss how to read from a Spark Streaming and merge/upsert data into a Delta Lake. We will also optimize/cluster data of the delta table. In the end, we will show how to start a streaming pipeline with the previous target table as the source. +Overall, the process works in the following manner, we read data from a streaming source and use this special function ***foreachBatch. + + + diff --git a/public/tags/z-order/page/1/index.html b/public/tags/z-order/page/1/index.html new file mode 100644 index 000000000..86bd7073d --- /dev/null +++ b/public/tags/z-order/page/1/index.html @@ -0,0 +1,10 @@ + + + + https://canadiandataguy.com/tags/z-order/ + + + + + + diff --git a/remoteconfig.template.json b/remoteconfig.template.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/remoteconfig.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index a74587342..91ecc4499 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -11,3 +11,5 @@ .box-simple { min-height: 230px; } + + diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 000000000..f010ab251 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,106 @@ +.social-share { + position: relative; + top: -0.5em; +} + +.social-share ul { + margin: 0; +} + +.social-share ul li p { + display: none; +} + +.social-share .share-icons li { + padding: 0 !important; + padding-bottom: 10px !important; +} + +.social-share .share-btn { + padding: 0.25em; + width: 3em; +} + +.social-share-nav .share-btn h3{ + color: #ffffff; +} + +ul.share-icons { + cursor: default; + list-style: none; + padding-left: 0; + margin-top: 1em; +} + +ul.share-icons li { + display: inline-block; + padding: 0 1em 0 0; +} + +ul.share-icons li:last-child { + padding-right: 0; +} + +ul.share-icons li > * { + text-decoration: none; + border: 0; +} + +ul.share-icons li > *:before { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + text-transform: none !important; +} + +ul.share-icons li > * .label { + display: none; +} + +.share-btn { + display: inline-block; + color: #ffffff; + border: none; + border-radius: 4px; + box-shadow: 0 2px 0 0 rgba(0,0,0,0.2); + outline: none; + text-align: center; + text-decoration: none; +} + +.share-btn:hover { + color: #ffffff !important; +} + +.share-btn:active { + position: relative; + top: 2px; + box-shadow: none; + color: #e2e2e2; + outline: none; +} +.share-btn .widget-social__link-icon { + margin: 0; +} + +.share-btn.twitter { background: #55acee; } +.share-btn.google-plus { background: #dd4b39; } +.share-btn.facebook { background: #3B5998; } +.share-btn.linkedin { background: #4875B4; } +.share-btn.stumbleupon { background: #EB4823; } +.share-btn.pinterest { background: #BD081C; } +.share-btn.reddit { background: #ff5700; } +.share-btn.email { background: #444444; } +.share-btn.whatsapp { background: #25d366; } + + +.share-btn.twitter:hover { background: #4c9ad6; } +.share-btn.google-plus:hover { background: #c64333; } +.share-btn.facebook:hover { background: #2f4779; } +.share-btn.linkedin:hover { background: #4069a2; } +.share-btn.stumbleupon:hover { background: #d3401f; } +.share-btn.pinterest:hover { background: #AD0000; } +.share-btn.reddit:hover { background: #e54e00; } +.share-btn.email:hover { background: #363636; } \ No newline at end of file diff --git a/static/css/style.default.css b/static/css/style.default.css index ba0eaa59d..65573e4ef 100644 --- a/static/css/style.default.css +++ b/static/css/style.default.css @@ -127,7 +127,7 @@ div.section { vertical-align: middle; text-align: center; color: #fff; - font-size: 12px; + font-size: 14px; line-height: 30px; border-radius: 15px; } @@ -199,7 +199,7 @@ ul.list-style-none { } .pages-listing .item .text p { color: #999999; - font-size: 12px; + font-size: 14px; margin-bottom: 20px; } .banner { @@ -249,7 +249,7 @@ ul.list-style-none { } #top p { margin: 0; - font-size: 12px; + font-size: 14px; } #top .social { float: right; @@ -262,7 +262,7 @@ ul.list-style-none { height: 24px; border-radius: 12px; line-height: 24px; - font-size: 12px; + font-size: 14px; text-align: center; } #top .social a:hover { @@ -290,7 +290,7 @@ ul.list-style-none { float: right; } #top .login a { - font-size: 12px; + font-size: 14px; color: #eeeeee; margin-right: 15px; text-decoration: none; @@ -368,7 +368,7 @@ ul.list-style-none { .navbar ul.dropdown-menu li a { position: relative; color: #999999; - font-size: 12px; + font-size: 14px; display: block; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; @@ -421,7 +421,7 @@ ul.list-style-none { .navbar .yamm-content ul li a { position: relative; color: #999999; - font-size: 12px; + font-size: 14px; display: block; -webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; @@ -542,13 +542,13 @@ ul.list-style-none { } .btn-sm { padding: 5px 10px; - font-size: 12px; + font-size: 14px; line-height: 1.5; border-radius: 0; } .btn-xs { padding: 1px 5px; - font-size: 12px; + font-size: 14px; line-height: 1.5; border-radius: 0; } @@ -934,7 +934,7 @@ fieldset[disabled] .btn-template-primary.active { } .panel.sidebar-menu .panel-body label { color: #999999; - font-size: 12px; + font-size: 14px; } .panel.sidebar-menu .panel-body label:hover { color: #555555; @@ -963,7 +963,7 @@ fieldset[disabled] .btn-template-primary.active { display: block; padding: 10px 15px; padding-left: 30px; - font-size: 12px; + font-size: 14px; color: #999999; } .panel.sidebar-menu ul.nav ul li a:hover, @@ -988,7 +988,7 @@ fieldset[disabled] .btn-template-primary.active { text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700; - font-size: 12px; + font-size: 14px; text-decoration: none; } .panel.sidebar-menu ul.tag-cloud li a:hover { @@ -1052,7 +1052,7 @@ fieldset[disabled] .btn-template-primary.active { .panel.sidebar-menu ul.popular li p.date, .panel.sidebar-menu ul.recent li p.date { float: right; - font-size: 12px; + font-size: 14px; color: #999999; } .panel.sidebar-menu ul.popular li:last-child, @@ -1060,7 +1060,7 @@ fieldset[disabled] .btn-template-primary.active { border-bottom: none; } .panel.sidebar-menu .text-widget { - font-size: 12px; + font-size: 14px; } .panel.sidebar-menu.with-icons ul.nav li a:after { font-family: 'FontAwesome'; @@ -1331,7 +1331,7 @@ fieldset[disabled] .btn-template-primary.active { .testimonials .item .testimonial .name-picture p { color: #999999; margin: 0; - font-size: 12px; + font-size: 14px; } .testimonials .item .testimonial .name-picture img { float: right; @@ -1354,7 +1354,7 @@ fieldset[disabled] .btn-template-primary.active { } .team-member p.role { color: #999999; - font-size: 12px; + font-size: 14px; text-transform: uppercase; letter-spacing: 0.06em; } @@ -1397,7 +1397,7 @@ fieldset[disabled] .btn-template-primary.active { } .team-member .text p { color: #999999; - font-size: 12px; + font-size: 14px; } .team-member .social, .team-member-detail .social { @@ -2081,7 +2081,7 @@ fieldset[disabled] .btn-template-primary.active { color: #999999; text-transform: uppercase; font-weight: 300; - font-size: 12px; + font-size: 14px; letter-spacing: 0.08em; } #blog-listing-medium .post .author-category a { @@ -2089,7 +2089,7 @@ fieldset[disabled] .btn-template-primary.active { } #blog-listing-medium .post .date-comments { float: right; - font-size: 12px; + font-size: 14px; } #blog-listing-medium .post .date-comments a { color: #999999; @@ -2159,7 +2159,7 @@ fieldset[disabled] .btn-template-primary.active { text-transform: uppercase; letter-spacing: 0.08em; font-weight: 300; - font-size: 12px; + font-size: 14px; } .box-image-text.blog .author-category a { font-weight: 500; @@ -2210,7 +2210,7 @@ fieldset[disabled] .btn-template-primary.active { } #blog-post .comment .posted { color: #999999; - font-size: 12px; + font-size: 14px; } #blog-post .comment .reply { font-family: "Roboto", Helvetica, Arial, sans-serif; @@ -2335,7 +2335,7 @@ fieldset[disabled] .btn-template-primary.active { transform: scale(1.1, 1.1); } .goToDescription { - font-size: 12px; + font-size: 14px; text-align: center; margin-bottom: 40px; } @@ -2600,7 +2600,7 @@ fieldset[disabled] .btn-template-primary.active { margin: 0; text-transform: uppercase; letter-spacing: 0.08em; - font-size: 12px; + font-size: 14px; } #footer .blog-entries .item .name h5 a { color: #eeeeee; @@ -2625,7 +2625,7 @@ fieldset[disabled] .btn-template-primary.active { background: #333; color: #ccc; padding: 50px 0; - font-size: 12px; + font-size: 14px; line-height: 28px; } #copyright p { @@ -3413,7 +3413,7 @@ p { } } .text-small { - font-size: 12px; + font-size: 14px; } .text-large { font-size: 18px; diff --git a/static/css/style.red.css b/static/css/style.red.css index 54e66ddfb..cb6ec295c 100644 --- a/static/css/style.red.css +++ b/static/css/style.red.css @@ -2182,6 +2182,7 @@ fieldset[disabled] .btn-template-primary.active { } #blog-post #post-content { margin-bottom: 20px; + font-size: 18px; } #blog-post #post-content img{ max-width: 100%; diff --git a/static/images/about/about-content-svg.svg b/static/images/about/about-content-svg.svg new file mode 100755 index 000000000..ac66d61ed --- /dev/null +++ b/static/images/about/about-content-svg.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/static/images/about/about-img.jpg b/static/images/about/about-img.jpg new file mode 100755 index 000000000..e99dcff68 Binary files /dev/null and b/static/images/about/about-img.jpg differ diff --git a/static/images/about/about-mask-svg.svg b/static/images/about/about-mask-svg.svg new file mode 100755 index 000000000..d0a7bb802 --- /dev/null +++ b/static/images/about/about-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/images/blog/blog-shape.svg b/static/images/blog/blog-shape.svg new file mode 100755 index 000000000..555b54535 --- /dev/null +++ b/static/images/blog/blog-shape.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/static/images/contact/abstract.png b/static/images/contact/abstract.png new file mode 100755 index 000000000..f8f1bb481 Binary files /dev/null and b/static/images/contact/abstract.png differ diff --git a/static/images/contact/github.svg b/static/images/contact/github.svg new file mode 100755 index 000000000..0862c4730 --- /dev/null +++ b/static/images/contact/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/contact/leetcode.png b/static/images/contact/leetcode.png new file mode 100755 index 000000000..8c25d29f7 Binary files /dev/null and b/static/images/contact/leetcode.png differ diff --git a/static/images/contact/linkedin.svg b/static/images/contact/linkedin.svg new file mode 100755 index 000000000..4e8aec29d --- /dev/null +++ b/static/images/contact/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/contact/location.svg b/static/images/contact/location.svg new file mode 100755 index 000000000..977777c1a --- /dev/null +++ b/static/images/contact/location.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/images/contact/profile-img-small.jpeg b/static/images/contact/profile-img-small.jpeg new file mode 100755 index 000000000..b070283df Binary files /dev/null and b/static/images/contact/profile-img-small.jpeg differ diff --git a/static/images/contact/resume.png b/static/images/contact/resume.png new file mode 100755 index 000000000..3599ec416 Binary files /dev/null and b/static/images/contact/resume.png differ diff --git a/static/images/favicon.png b/static/images/favicon.png new file mode 100755 index 000000000..c54eaf2ac Binary files /dev/null and b/static/images/favicon.png differ diff --git a/static/images/hero/bkp.jpg b/static/images/hero/bkp.jpg new file mode 100755 index 000000000..28636fba4 Binary files /dev/null and b/static/images/hero/bkp.jpg differ diff --git a/static/images/hero/chicago.jpg b/static/images/hero/chicago.jpg new file mode 100755 index 000000000..26143ff84 Binary files /dev/null and b/static/images/hero/chicago.jpg differ diff --git a/static/images/hero/figure-svg copy.svg b/static/images/hero/figure-svg copy.svg new file mode 100755 index 000000000..3e125716f --- /dev/null +++ b/static/images/hero/figure-svg copy.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/hero/figure-svg.svg b/static/images/hero/figure-svg.svg new file mode 100755 index 000000000..d9e69a988 --- /dev/null +++ b/static/images/hero/figure-svg.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/hero/heart-svg.svg b/static/images/hero/heart-svg.svg new file mode 100755 index 000000000..423372804 --- /dev/null +++ b/static/images/hero/heart-svg.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/hero/hero-background-svg.svg b/static/images/hero/hero-background-svg.svg new file mode 100755 index 000000000..1ed75aa6d --- /dev/null +++ b/static/images/hero/hero-background-svg.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/static/images/hero/hero-mask-svg.png b/static/images/hero/hero-mask-svg.png new file mode 100755 index 000000000..011e0f506 Binary files /dev/null and b/static/images/hero/hero-mask-svg.png differ diff --git a/static/images/hero/hero-mask-svg.svg b/static/images/hero/hero-mask-svg.svg new file mode 100755 index 000000000..c89fa6a52 --- /dev/null +++ b/static/images/hero/hero-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/images/hero/popup-thumb.png b/static/images/hero/popup-thumb.png new file mode 100755 index 000000000..46799f0f6 Binary files /dev/null and b/static/images/hero/popup-thumb.png differ diff --git a/static/images/hero/signatureupdate.gif b/static/images/hero/signatureupdate.gif new file mode 100755 index 000000000..aef8e030f Binary files /dev/null and b/static/images/hero/signatureupdate.gif differ diff --git a/static/images/latest-post/latest-post-1.png b/static/images/latest-post/latest-post-1.png new file mode 100755 index 000000000..88c824d3a Binary files /dev/null and b/static/images/latest-post/latest-post-1.png differ diff --git a/static/images/latest-post/latest-post-2.png b/static/images/latest-post/latest-post-2.png new file mode 100755 index 000000000..06b32a345 Binary files /dev/null and b/static/images/latest-post/latest-post-2.png differ diff --git a/static/images/latest-post/latest-post-3.png b/static/images/latest-post/latest-post-3.png new file mode 100755 index 000000000..ae6f1ded7 Binary files /dev/null and b/static/images/latest-post/latest-post-3.png differ diff --git a/static/images/pin.png b/static/images/pin.png new file mode 100755 index 000000000..2ccf076bf Binary files /dev/null and b/static/images/pin.png differ diff --git a/static/images/portfolio/ProsperLoan.html b/static/images/portfolio/ProsperLoan.html new file mode 100755 index 000000000..f4e2d6ead --- /dev/null +++ b/static/images/portfolio/ProsperLoan.html @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    +

    PROSPER LOAN

    +
    +
    +

    by MAY CHU

    +

    ========================================================

    +
    +
    +

    ABSTRACT

    +

    This project aims to use Exploratory Data Analysis (EDA) to explore the Prosper Loan dataset which includes over 113937 listings of Prosper borrowers.

    +

    Prosper is one of the leading online Peer-to-peer lending platform that connects potential borrowers with potential lenders, filters candidates on both sides of the equation, and sets some rules. Peer-to-peer lending is the new trend in recent years, which allows people to seek out a loan from somewhere other than a traditional banking institution. Instead of going to a bank or a credit union, borrowers can get a loan that comes from individuals looking to earn more interest on their money than promised by traditional savings accounts.

    +

    Here how it works:

    +
      +
    • Borrowers choose a loan amount, purpose and post a loan listing.
    • +
    • Investors review loan listings and invest in listings that meet their criteria.
    • +
    • Once the process is complete, borrowers make fixed monthly payments and investors receive a portion of those payments directly to their Prosper account.
    • +
    +

    This project tends to answer 4 questions:

    +
      +
    1. What is the population of Prosper borrowers?
    2. +
    3. What factors affect the number of investors to prosper loan?
    4. +
    5. What affect borrower rate and APR?
    6. +
    7. Loss in charge-off loans?
    8. +
    +
    +
    +

    Univariate Plots Section

    +
    +

    A. Listing summary

    +
    +

    I. Loan term

    +
    ## Source: local data frame [3 x 2]
    +## 
    +##    Term     n
    +##   (int) (int)
    +## 1    12  1614
    +## 2    36 87778
    +## 3    60 24545
    +
    ## [1] 0.7704082
    +

    There are only 3 loan terms in the dataset: 12,36,60 months. Loans with duration of 36 months (3 years) is the most popular with 87,778 observations (account for 77%).

    +
    +
    +

    II. Loan original amount

    +

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1000    4000    6500    8337   12000   35000
    +
    ## [1] 430
    +
    ## [1] 867
    +

    75% of the loans smaller than $12000, 50% lie between $4000 to $12000. The maximum loan amount Prosper allows is $35,000. There are 430 loans with the amount of $350,000. Not all loans are fully funded, there are 867 partially funded loans. The status of partial funded loans are as the chart below

    +

    +
    +
    +

    III. Loan purpose

    +

    +

    There are 20 loans purpose: 0 - Not Available, 1 - Debt Consolidation, 2 - Home Improvement, 3 - Business, 4 - Personal Loan, 5 - Student Use, 6 - Auto, 7- Other, 8 - Baby&Adoption, 9 - Boat, 10 - Cosmetic Procedure, 11 - Engagement Ring, 12 - Green Loans, 13 - Household Expenses, 14 - Large Purchases, 15 - Medical/Dental, 16 - Motorcycle, 17 - RV, 18 - Taxes, 19 - Vacation, 20 - Wedding Loans

    +

    The most common purpose is for Debt consolidation (50%), about 15% of borrowers don’t specify their purpose.

    +
    +
    +

    IV. Estimated Performance of the loan

    +
    +
    1.Prosper rating
    +

    The Prosper Rating assigned at the time the listing was created between HR (worst) - AA (best). Applicable for loans originated after July 2009. Prosper Ratings allow potential investors to easily consider a loan application’s level of risk because the rating represents an estimated average annualized loss rate range to the investor.

    +

    +
    ## Source: local data frame [8 x 2]
    +## 
    +##   ProsperRating..Alpha.     n
    +##                  (fctr) (int)
    +## 1                    HR  6935
    +## 2                     E  9795
    +## 3                     D 14274
    +## 4                     C 18345
    +## 5                     B 15581
    +## 6                     A 14551
    +## 7                    AA  5372
    +## 8                    NA 29084
    +

    Prosper rating of C is the most count (represent 16%). Only 5000 borrowers have prosper rating of AA (which is the least count). 26% of the dataset (29084 listings) has blank Prosper Rating. As we know that Prosper Rating was just introduced after 2009. Before that, Prosper used the Credit Grade as the reference for borrowers’ credit. The distribution of Credit Grade is shown in the plot below.

    +

    +
    +
    +
    +

    2. Lender’s yield, Estimated Effective Yield, Estimated Loss and Estimated Return

    +

    +

    Lender yield = interest rate - servicing fee Lender yield clusters around 0.05 to 0.3 (5% - 30%)

    +

    Effective yield = borrower interest rate - the servicing fee rate - estimated uncollected interest on charge-offs + estimated collected late fees. Most Effective yield fluctuates around 0.1 to 0.3 (10%-30%)

    +

    Estimated loss is the estimated principal loss on charge-offs. Most of its values get around 0 to 0.2% (0-20%)

    +

    Estimated return = Estimated Effective Yield - Estimated Loss Rate. Estimated return value clusters around 10%

    +
    +
    +
    +

    B. Borrower’s information

    +
    +

    I. Borrower’s location

    +

    +

    15000 borrowers come from California, which rank it the first place among borrower origin states. States of more than 5000 borrowers include: Georgia, Illinois, Florida, New York and Texas

    +
    +
    +

    II. Occupation

    +

    +

    The are 68 occupations in the list. Many borowers don’t specify their occupations (with category Other). There are also missing values

    +
    +
    III. Employment status
    +

    +

    Most of the borrowers are employed (60%), work full-time(20%),part-time (2%) and self-employed (5%)… There are only 835 borrowers unemployed (very small percentage). There are missing values, Other and Not available answer which make it difficult to determine these borrowers’ employment status.

    +
    +
    +
    Employment status duration
    +

    Employment status duration is the length in months of the employment status at the time the listing was created.

    +

    +

    To have a more general view of employment duration, I will join the duration by years by bucket

    +
    ## 
    +##   (0,5]  (5,10] (10,15] (15,20] (20,25] (25,30] (30,35] (35,40] 
    +##   48076   25624   14194    7362    4449    2715    1408     604
    +

    +

    From the chart, we can see that nearly 5000 borrowers have stayed in their current status from 1 to 5 years. However, knowing this number doesn’t make any sense if we don’t know which status they are in. So, I will color the employment status against this bar chart

    +

    +

    It’s clear that most borrowers are employed and full-time employed. Over 30,000 borrowers have been employed from 1-5 years. Unemployed borrowers only stay in that status for less than 5 years.

    +
    +
    +
    +

    IV. Income Range

    +

    +
    ##   Not employed  Not displayed             $0      $1-24,999 $25,000-49,999 
    +##            806           7741              0           7274          32192 
    +## $50,000-74,999 $75,000-99,999      $100,000+           NA's 
    +##          31050          16916          17337            621
    +

    Over 60000 borrowers earn from 25000 to 75000 annually (account for half of the dataset)

    +
    +
    Income Range vs. Income Verifiable
    +

    +

    Only a very small number of borrowers cannot verify income. The only exception appears in Not-employed group, but the reason is quite obvious.

    +
    +
    +
    +

    V. Homeownership

    +
    ## Source: local data frame [2 x 2]
    +## 
    +##   IsBorrowerHomeowner     n
    +##                 (lgl) (int)
    +## 1               FALSE 56459
    +## 2                TRUE 57478
    +

    The number of borrowers who own a house is similar to those who don’t own the house. It seems having a collateral doesn’t affect a Prosper loan listing

    +
    +
    +

    V. Borrower’s Credit Information

    +
    +
    1. Prosper Score
    +

    Prosper Score is a custom risk score built using historical Prosper data. The score ranges from 1-11, with 11 being the best, or lowest risk score. Applicable for loans originated after July 2009.

    +

    +

    The most common Prosper Score is 4, 6, 8 (account for nearly 15% each)

    +
    +
    +
    2.Credit Score
    +

    +

    Similar to prosper rating and prosper score, many borrowers have medium credit score range from 640 to 779. Very few have scores lower than 640 or higher than 780.

    +
    +
    +
    3.Debt/Income Ratio
    +

    The debt to income ratio of the borrower at the time the credit profile was pulled. This value is Null if the debt to income ratio is not available. This value is capped at 10.01 (any debt to income ratio larger than 1000% will be returned as 1001%).

    +

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    +##   0.000   0.140   0.220   0.276   0.320  10.010    8554
    +
    ## [1] 272
    +

    There are 272 outliers with debt/income ratio = 1001%. However, most borrowers have debt to income ratio smaller than 0.5. The most occurrence is around 0.1 to 0.25.

    +
    +
    +
    4. Credit history
    +

    Current Deliquencies: Number of accounts delinquent at the time the credit profile was pulled.

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    +##  0.0000  0.0000  0.0000  0.5921  0.0000 83.0000     697
    +

    +

    87.7% Prosper Borrowers (100,000) have no current delinquenct accounts at the time they created the listing.

    +

    Inquiries Last 6 Months: Number of inquiries in the past six months at the time the credit profile was pulled. Inquiries are the number of times borrowers attempted to borrow money in the past six months

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    +##   0.000   0.000   1.000   1.435   2.000 105.000     697
    +

    +

    Bank card utilization: The percentage of available revolving credit that is utilized at the time the credit profile was pulled.

    +

    +

    There are nearly 7000 borrowers with 0 bank card utilization. However, among 99,551 (87.4%) borrowers who use their credit cards, there are 63% have the bank card utilization more than 50%. There are 1744 borrowers whose bank card utilization larger than 100%.

    +
    +
    +
    6.Prior Prosper Loan Performance
    +
    ## [1] 22084
    +
    ## [1] 0.1938264
    +

    +
    ## Source: local data frame [10 x 2]
    +## 
    +##    TotalProsperLoans     n
    +##                (int) (int)
    +## 1                  0     1
    +## 2                  1 15538
    +## 3                  2  4540
    +## 4                  3  1447
    +## 5                  4   417
    +## 6                  5   104
    +## 7                  6    29
    +## 8                  7     8
    +## 9                  8     1
    +## 10                NA 91852
    +

    There are 22084 borrowers have prior prosper loans, which account for 19%. Among them, 70% have only 1 prior prosper loans

    +
    ## Source: local data frame [3 x 2]
    +## 
    +##   prior.prosper.performance < 1     n
    +##                           (lgl) (int)
    +## 1                         FALSE 18214
    +## 2                          TRUE  3806
    +## 3                            NA 91917
    +
    ## 
    +##     FALSE      TRUE 
    +## 0.8271571 0.1728429
    +

    Among 22084 borrowers who have prior prosper loans, there are 18214 borrowers pay on time (82.7%), 3806 (17.3%) borrowers have overdue

    +
    +
    +
    +
    +

    C. Loan Activity

    +
    +

    I. Loans actual performance

    +

    Loan status

    +

    +

    There are over 56576 Current loans (50%), over 38074 completed loans (35%) . 19077 loans have delinquent which account for 16,7% consisting of: 2067 cases deliquent from 1 to 120 days past-due, 5018 default (121+ days past due) and 11992 Chargeoff loans (150+ days past dues, no expectation of sufficient payment)

    +
    ## [1] 16902
    +
    ## [1] 0.1483451
    +

    Number of Gross principal Loss cases: 16902 account for 14%

    +
    +
    +

    II. Borrower rate (Interest)

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##  0.0000  0.1340  0.1840  0.1928  0.2500  0.4975
    +

    +

    Mean interest rate is 19.28%. Haft of the rate is about 13.4% to 25%. The distribution is quite bimodal, which two modes of frequent interest rate: from 14-16% to 31-32%, which is pretty high.

    +

    Do the borrower rate diffirent with different original loan quarter? To discover this, I’ll graph the mean of borrower rate against loan origination quarter and year

    +

    +
    +
    +

    III. Borrower APR

    +

    Borrower APR is the Borrower’s Annual Percentage Rate (APR) for the loan. APR refers to the total cost of borrowing, as the calculation for APR includes not only the interest rate, but also many other fees the borrower might be charged. So APR is seen as the “effective interest rate,” a way for borrowers to compare one loan to another.

    +

    +

    The Borrower APR distribution looks similar to that of borrower rate, although the histogram shifts rightward, indicating that the APR is generally larger than interest rate. But how large is the difference?

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    +##   0.000   1.900   2.502   2.604   3.653  14.930      25
    +

    +

    As the APR is equal to interest rate plus fees. The most common fees are around 0.5-1%, 2-3% and 3.5-4%. What cause the difference in fees?

    +
    +
    +

    IV. Investors

    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    2.00   44.00   80.48  115.00 1189.00
    +

    +

    75% of loans have less than 115 investors. There are outliers, however, some loans have more than 1000 investors. I wonder what make these loans so attractive. The distribution of investor counts is right-skewed. To make it normal, I’ll transform it to log10 to reduce skewness

    +

    +
    ## Source: local data frame [2 x 2]
    +## 
    +##   Investors == 1     n
    +##            (lgl) (int)
    +## 1          FALSE 86123
    +## 2           TRUE 27814
    +

    There are over 27000 loans with only 1 investors, I will cut that out of the histogram to have a better feel of investor distribution

    +

    +
    +
    +
    +
    +

    Univariate Analysis

    +
    +

    What is the structure of your dataset?

    +

    This data set contains 113,937 loans with 81 variables on each loan. The variables can be classified into 3 categories: 1. Listing summary: the summary of information about the loan application based on which lenders can make investing decisions (prosper rating, loan term, loan amount, loan purpose and estimated performance of a loan such as estimated effective yield, estimated lender yield, estimated loss rate and estimated return rate) 2. Borrower’s information: borrowers’ geography, employment status and status duration, prosper score, borrower’s credit history such as credit score, prosper score, number of credit lines and current/past delinquencies… 3. The loan activity: the status of loans, number of investors, the borrower rate/APR and the loans’ performance

    +
    +
    +

    What is/are the main feature(s) of interest in your dataset?

    +
      +
    • The number of investors participate in a loan
    • +
    • The interest rate (borrower rate) and APR of the loan
    • +
    • The underperformed loans (charge-off loans)
    • +
    +
    +
    +

    What other features in the dataset do you think will help support your investigation into your feature(s) of interest?

    +
      +
    • What factors deciding the borrower rate and APR of a loan: prosper rating, estimated loss rate and loan term…
    • +
    • What attract investors to a loan: I suspect that investor may look at prosper rating, lender yield, return yield, employment status, debt to income ratio, credit rating… to decide whether a loan is rewarding
    • +
    • Common features of underperfomed loans: prosper rating, debt to income ratio, delinquencies history, proser loan history or public record…
    • +
    +
    +
    +

    Did you create any new variables from existing variables in the dataset?

    +

    I created several new variables - difference.in.rate: show the difference between borrower APR and borrower rate - duration.by.bucket: join the duration of employment status duration into different intervals - rate.vs.time: group the dataset by quarter and year to compare the borrower’s rate - CreditRange: a range of lower and upper Credit Score - prior.prosper.performance: the performance of the prior prosper loan, which represent the ratio of on time payment on total billed payments. - CurrentDelinquence.bucket, Delinquence7year.bucket, Inquiries6month.bucket

    +
    +
    +

    Of the features you investigated, were there any unusual distributions? Did you perform any operations on the data to tidy, adjust, or change the form of the data? If so, why did you do this?

    +

    The investor distribution is right-skewed. So I take the log10 of the number of investors to create a more normal distribution. I hope to build a regression model for this variable later.

    +
    +
    +
    +

    Bivariate and Multivariate Plots Section

    +
    +

    I. Prosper Rating & Estimated Loss Rate

    +

    Prosper Rating represents an estimated average annualized loss rate range to the investor. The estimated loss rate is based on the historical performance of Prosper loans with similar characteristics.

    +

    +

    Prosper Rating vs. Estimated Avg. Annual Loss Rate

    +
      +
    • AA 0.00 - 1.99%
    • +
    • A 2.00 - 3.99%
    • +
    • B 4.00 - 5.99%
    • +
    • C 6.00 - 8.99%
    • +
    • D 9.00 - 11.99%
    • +
    • E 12.00 - 14.99%
    • +
    • HR > 15.00%
    • +
    +
    +
    +

    Estimated Loss by Prosper Score and Credit Range

    +

    +

    Higher credit score, lower estimated loss. Estimated loss’ means drops with the increase in credit range. The same phenomenon can be observed in the effect of Prosper Score on Estimated loss

    +

    +

    +

    Clearly, mean estimated loss goes down with the increase in Prosper Rating and Credit Range, which shows a strong negative linear relationship.

    +
    +
    +

    Prosper Score

    +

    The Prosper score estimates the probability of a loan going “bad,” where “bad” is the probability of going 60+ days past due within the first twelve months from the date of loan origination. Applicants from April, 2008 through April, 2011 were used to build the discrete additive scorecard. The scorecard was verified and results validated on an independent validation sample of loans booked during the same time period as well as a more recent sample of loans booked outside of the loan development period.

    +

    Key variables in the scorecard are: - Number of inquiries on the credit bureau: InquiriesLast6Months - Number of delinquent accounts on the credit bureau: CurrentDelinquencies - Credit card utilization on the credit bureau: BankcardUtilization - Number of recently opened trades on the credit bureau: TradesOpenedLast6Months - Debt to income ratio: DebtToIncomeRatio - Loan payment performance on prior Prosper loans

    +

    +

    +

    +

    +

    Trying to build a sub dataset from April 2008 to April 2011 and graphing the key variables in scorecard against the Prosper Score, I can see that Prosper Score has negative relationship with inquiries last 6 month, current delinquencies, bankcard utilization and debt to income ratio… However, this relationship is not clear and hard to quantify.

    +
    +
    +

    II. What affect the loan interest rates

    +
    +

    Prosper rating and Credit Grade on Borrower Rate

    +

    +

    Clearly, Borrower Rate decreases when Prosper Rating increases. Before July 2009, Prosper used Credit Grade to calculate Borrower Rate. I’ll use graph Borrower Rate on Credit Grade for loan from 2005 to 2009

    +

    +

    Although the same range of Borrower rate is assigned to the same level of credit grade and prosper rating, the fundamental difference is the way credit grade and prosper rating determined. Credit Grade is based only on credit bureau score (AA: 760+, HR: 520-559) Prosper Rating takes in various credit information of borrowers including prior prosper loans performance

    +
    +
    +

    Estimated Loss on Borrower Rate

    +

    +
    ## [1] 0.945297
    +

    When estimated loss increases, borrower rate also increases and the borrower rate becomes more disperse. The correlation between estimated loss and borrower rate is high 94.5%, showing a strong and positive correlation

    +
    +
    +

    Estimated Loss on Borrower Rate color by Prosper Rating

    +

    +
    +
    +

    Estimated Loss on Borrower Rate, facet by Term

    +

    +

    Borrower Rate is positively correlated with estimated loss. Higher prosper rating borrowers have lower borrower rate. We can see clearly that different prosper rating is associated with a specific borrower rate. Loan with term of 36 months have borrower rate most dispersed, maybe because most loans have term of 36 months. The other terms show perfect correlation.

    +
    +
    +

    What affect the difference between borrower rate and borrower APR

    +

    +
    ## dt$Term: 12
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   0.935   5.728   7.772   6.614   7.945  14.930 
    +## -------------------------------------------------------- 
    +## dt$Term: 36
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    +##   0.000   1.423   2.816   2.592   3.708   6.395      25 
    +## -------------------------------------------------------- 
    +## dt$Term: 60
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   0.215   2.319   2.408   2.385   2.524   2.798
    +

    It’s clear that the difference between borrower rate and APR is determined by term. 50% of Loans with duration of 1 year has range from 5.7% to 7.9%; 3 years from 1.423 to 3.7%; and 5 years from 2.3 to 2.5%. This difference also fluctuates with Prosper Rating. We can see that only loans with high Prosper Rating can apply for term of 5 years from the chart below

    +

    +
    ## dt$ProsperRating..Alpha.: HR
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   2.210   3.746   4.027   3.874   4.027   6.395 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: E
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   2.174   3.631   3.891   3.721   3.948  10.080 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: D
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   2.131   2.604   3.542   3.416   3.825   9.927 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: C
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   2.095   2.440   3.393   3.169   3.722   9.835 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: B
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   1.376   2.321   2.730   2.958   3.626   8.430 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: A
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   1.340   2.252   2.788   2.597   2.819   7.674 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: AA
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   0.215   0.343   1.336   1.092   1.346   3.562
    +

    +
    +
    +
    +

    Number of investors sponsoring a loan

    +

    What draws investors to a loan

    +

    +

    From this chart, I can see very good correlation among borrower rate, borrower APR, lender yield, effective yield and estimated return. This is obvious because these rates are calculated from each other. For example, lender’s yield is equal borrower rate less servicing fee. However, what astounds me the most is that these variables have very weak correlation with the investor count. For instance, correlation between lender’s yield and investors is only -0.249. This is beyond my expectation.

    +

    When continuous variables show little effect on investor number, I’ll try to graph the investor number against nominal variables such as prosper rating, income range, although relationship between nominal and continuous variables can be difficult to quantify.

    +

    Prosper Rating on Investor counts:

    +

    +
    ## dt$ProsperRating..Alpha.: HR
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    9.00   31.00   35.28   54.00  237.00 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: E
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    2.00   25.00   35.01   55.00  279.00 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: D
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    4.00   35.00   56.23   87.00  511.00 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: C
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    1.00    9.00   51.09   78.00 1024.00 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: B
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##     1.0     1.0    19.0    69.8   112.0   856.0 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: A
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00    1.00   51.00   97.53  162.00 1189.00 
    +## -------------------------------------------------------- 
    +## dt$ProsperRating..Alpha.: AA
    +##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##    1.00   62.75  150.00  178.40  274.00 1035.00
    +

    The better Prosper Rating, the more investors. For example: for rating AA, 50% of Investor count range from 63 to 274 with mean 150. For rating HR, 50% of Investor count range from 9 to 54 with mean 35 investors.

    +

    Income Range on Investor counts

    +

    +

    Investors number get a bit higher with the increase in income range. The same thing can be observed between investor number and debt to income ratio

    +

    Debt to Income Ratio on Investor count

    +

    +

    Borrowers who have debt to income ratio lower than 1 attract most investors (risk averse). However, there’re investors who accept very high debt to income ratio of 10.01 (which is larger than 1000 percent). Also this relationship is not linear, which explains why our correlation coefficient is very poor (0.00176)

    +

    Lender Yield on Investor count

    +

    +

    Investors don’t prefer very high lender yield because high lender yield means high borrower rate, means high risk. That’s why we can see the number of investors go down when lender yield increase. Most preferred rate is around 10%

    +

    +

    Estimated return on Investor count

    +

    +

    Similar to lender yield, investors prefer estimated return around 10%. When they invest in lower Prosper Rating loans, they expect higher return (10-20%). But there are also investors accept negative estimated returns and invest in HR loans.

    +

    Loans with low and high number of investors:

    +

    +

    The popular rating for loans with only 1 investors is C with 8000 loans.The popular rating for loans with more than 100 investors are A with approximately 5500 counts.

    +
    +
    +

    Underperformed loans

    +

    This section we will work with underperformed loans (charged-off loans) and discover their common patterns

    +
    ## [1] 11992
    +
    ## [1] 0.1052511
    +

    There are 11992 charged off loans (account for 10.5% )

    +

    +

    Most borrowers with chargeoff loss have Prosper Rating lower than D and estimated loss larger than 10%.

    +

    Performance of charged off loan

    +

    +
    ## 
    +##     FALSE      TRUE 
    +## 0.1882922 0.8117078
    +

    For both Gross Principal Loss and Net Principal Loss, the thickest area lies from 50% to 100% and under 100 investors. Nearly 81% of Net percentage loss is over 50%.

    +

    Facet the chart using Prosper Rating, we see how Prosper Rating affect the investors number to a loan (a pattern we have discovered earlier). Low investor count usually go with low Prosper rating, and those loans with low ratings tend to have high percentage loss when it comes to charge off loans

    +

    +

    There are some cases, however, that the investors can recover of their investments. In those cases, Net Principal Loss is smaller than Gross Principal Loss. We can observe the phenomenon more clearly in the chart below where many points lie below the equate line of gross principal loss and net principal loss

    +

    +

    This brings me to a question of how accurate the estimated loss Prosper calculated in comparison to the actual loss investors have to suffer

    +

    +

    Sadly, the forecast is not even close. The actual loss is much higer than the estimated loss that Prosper uses to determine Prosper Rating based on such investors may make their decision.

    +
    +
    +
    +

    Bivariate and Multivariate Analysis

    +
    +

    Talk about some of the relationships you observed in this part of the investigation. How did the feature(s) of interest vary with other features in the dataset?

    +

    We learn from Loan purpose that most borrowers come to Prosper to find a means to settle their debt. This explains why Bank card utilization of most borrowers is high, and most of them don’t have very good prosper score, credit range as well as prosper rating.

    +

    Prosper uses Prosper Score and Credit Score to determine the Estimated Loss Rate. Estimated Loss Rate is then employed to calculate the Borrower Rate and to determine Prosper Rating. Term affects Borrower APR when Prosper imposes different service fee on different loan terms borrowers apply for.

    +

    Lender yield, Estimated effective yield and Estimated return are all derived from Borrower Rate. That’s why these rates show high correlation to each other. And because they are inter-dependent, it’s not useful to throw them together to predict effect on another variable (for example: investor count). At best, we can only use one of them as the predictor (lender yield or borrower rate, estimated loss rate). However, the correlation between such rate and investor count is negative and weak (~ -0.3%). This can infer that lenders participating to Prosper Loans are quite risk adverse. Still I cannot find any factors which affect investors’ decision strongly. Prosper Rating is a good indicator, but not a dominant factor. Hence, I think investor’s choice to invest in a loan is quite intuitive.

    +

    When investigating charged-off loans, I also realize that the estimated loss rate will not accurately predict the actual loss rate. Therefore, investors should be careful to base their decisions on such information.

    +
    +
    +

    What was the strongest relationship you found?

    +

    The strongest relationship I’m able to observe in this dataset is between estimated loss rate and borrower rate. Hence, I will build linear model to predict borrower rate based on estimated loss rate

    +
    +
    +
    +

    Prediction Model

    +
    +

    Linear Model of Borrower Rate

    +

    Now I will build a model to predict the Borrower Rate based on Estimated Loss Rate as they show good correlation to each other. As Estimated Loss was only calculated after July 2009, I will predict based on a sub dataset after July 2009

    +
    ## 
    +## Calls:
    +## m1: lm(formula = BorrowerRate ~ EstimatedLoss, data = dtAfter2009)
    +## 
    +## ===========================
    +## (Intercept)       0.075*** 
    +##                  (0.000)   
    +## EstimatedLoss     1.509*** 
    +##                  (0.002)   
    +## ---------------------------
    +## R-squared             0.894
    +## adj. R-squared        0.894
    +## sigma                 0.024
    +## F                712518.799
    +## p                     0.000
    +## Log-likelihood   194862.855
    +## Deviance             50.292
    +## AIC             -389719.710
    +## BIC             -389691.664
    +## N                 84853    
    +## ===========================
    +

    So let assume a listing has Estimated Loss of 0.0049, what is the estimated value of borrower rate given confidence level of 95%

    +
    ##          fit        lwr       upr
    +## 1 0.08226263 0.03454436 0.1299809
    +

    Borrower Rate of this listing has mean value of 0.08, and 95% chance that the rate will be from 0.035 to 0.13. 89.4% variability in Borrower Rate can be explained by that in Estimated Loss. This model only uses Estimated Loss as the predictor because (1) it has good correlation coefficient with Investor counts (2) as Estimated loss is derived from many other variables in the dataset (Prosper Score, Credit Range, borrowers’ credit history, Prosper Rating), throwing those variables into the model can cause multicollinearity.

    +
    +
    +

    Multilinear Model of Estimated Loss

    +
    ## 
    +## Calls:
    +## l1: lm(formula = EstimatedLoss ~ ProsperScore, data = dtAfter2009)
    +## l2: lm(formula = EstimatedLoss ~ ProsperScore + CreditRange, data = dtAfter2009)
    +## 
    +## =====================================================
    +##                                   l1          l2     
    +## -----------------------------------------------------
    +## (Intercept)                     0.159***    0.191*** 
    +##                                (0.000)     (0.001)   
    +## ProsperScore                   -0.013***   -0.011*** 
    +##                                (0.000)     (0.000)   
    +## CreditRange: 620-639/600-619               -0.007*** 
    +##                                            (0.001)   
    +## CreditRange: 640-659/600-619               -0.020*** 
    +##                                            (0.001)   
    +## CreditRange: 660-679/600-619               -0.033*** 
    +##                                            (0.001)   
    +## CreditRange: 680-699/600-619               -0.041*** 
    +##                                            (0.001)   
    +## CreditRange: 700-719/600-619               -0.049*** 
    +##                                            (0.001)   
    +## CreditRange: 720-739/600-619               -0.054*** 
    +##                                            (0.001)   
    +## CreditRange: 740-759/600-619               -0.057*** 
    +##                                            (0.001)   
    +## CreditRange: 760-779/600-619               -0.060*** 
    +##                                            (0.001)   
    +## CreditRange: 780-799/600-619               -0.065*** 
    +##                                            (0.001)   
    +## CreditRange: 800-819/600-619               -0.067*** 
    +##                                            (0.001)   
    +## CreditRange: 820-839/600-619               -0.066*** 
    +##                                            (0.001)   
    +## CreditRange: 840-859/600-619               -0.068*** 
    +##                                            (0.002)   
    +## CreditRange: 860-879/600-619               -0.067*** 
    +##                                            (0.003)   
    +## CreditRange: 880-899/600-619               -0.065*** 
    +##                                            (0.008)   
    +## -----------------------------------------------------
    +## R-squared                           0.454       0.545
    +## adj. R-squared                      0.454       0.545
    +## sigma                               0.035       0.032
    +## F                               70527.597    6764.427
    +## p                                   0.000       0.000
    +## Log-likelihood                 165141.180  172849.094
    +## Deviance                          101.331      84.497
    +## AIC                           -330276.360 -345664.187
    +## BIC                           -330248.314 -345505.260
    +## N                               84853       84853    
    +## =====================================================
    +
    +
    +
    +
    +

    Final Plots and Summary

    +
    +

    Plot One: Estimated Loss by Prosper Score and Credit Range

    +

    +
    +
    +

    Description One

    +

    This plot shows the effect of Prosper Score and Credit Range on Mean estimated loss. Estimated loss is higher with lower Prosper Score and Credit Range.

    +
    +
    +

    Plot Two:

    +

    Borrower Rate and Borrower APR by Estimated Loss and Term

    +

    +
    +
    +

    Description Two

    +

    The second plot answers the question of what influence the Borrower Rate and Borrower APR. Since we can see the good linear relationship between Estimated Loss and Borrower Rate, as well as how Term affects the difference between Borrower Rate and APR, we have a better idea of what Prosper will look at when determining Borrower Rate and Borrower APR

    +
    +
    +

    Plot Three:

    +

    +
    +
    +

    Description Three

    +

    Watch out!!! Forecast is just forecast. The estimated loss rate is much lower than the actual loss rate when it comes to charge-off loans. As Prosper uses estimated loss rate to determine Prosper Rating, borrower rate and consequently lender’s yield as well as estimated return; lenders who merely base their decision on those factors are risking losing more than what they expect.

    +
    +
    +
    +
    +

    Reflection

    +

    There are 81 variables in this dataset, but the variables used to make lending decisions are boiled down to several ones including Prosper Rating and Estimated Loss Rate; Prosper Score and Credit Range; Lender yield, estimated effective yield and estimated return… I wonder where borrowers’ information such as credit history, bankcard utilizaton, delinquencies,.. go into this process?

    +

    Prosper claims that they use a discrete scorecard built on applications from April 2008 to April 2011 to determine the Prosper Score. The scorecard takes in variables of borrowers’ credit information. However, graphing the Prosper Score against these variables, I see negative correlation but not enough to quantify. Lacking of domain knowledge, I was unable to build and test the effectiveness of this scorecard in determining Prosper Score, which in turn affects Estimated Loss Rate. Keeping in mind that Estimated Loss Rate is then used to decide Borrower Rate and Prosper Rating. The accuracy of the score card is critical to enhance Prosper’s ability to estimate yield and return for investors. However, from the dataset we can see that the accuracy of estimated loss rate is very poor. Investors should be careful when using these factors in their decision making process.

    +

    I didn’t build a model to predict investor number, because the correlation of various factors on investor counts is weak. Building a model with such low R square can be ineffective. Instead, I build a linear model to determine Estimated Loss Rate with good R square of 89.9%.

    +
    + + +
    + + + + + + + + diff --git a/static/images/portfolio/case-details.png b/static/images/portfolio/case-details.png new file mode 100755 index 000000000..14c6d9618 Binary files /dev/null and b/static/images/portfolio/case-details.png differ diff --git a/static/images/portfolio/deep_learning.jpeg b/static/images/portfolio/deep_learning.jpeg new file mode 100755 index 000000000..f4d52473f Binary files /dev/null and b/static/images/portfolio/deep_learning.jpeg differ diff --git a/static/images/portfolio/predictchurn.jpeg b/static/images/portfolio/predictchurn.jpeg new file mode 100755 index 000000000..d309f7fe2 Binary files /dev/null and b/static/images/portfolio/predictchurn.jpeg differ diff --git a/static/images/portfolio/seattle.jpeg b/static/images/portfolio/seattle.jpeg new file mode 100755 index 000000000..223d5db56 Binary files /dev/null and b/static/images/portfolio/seattle.jpeg differ diff --git a/static/images/portfolio/stockprice.jpeg b/static/images/portfolio/stockprice.jpeg new file mode 100755 index 000000000..3026f9611 Binary files /dev/null and b/static/images/portfolio/stockprice.jpeg differ diff --git a/static/images/screenshots/aks.png b/static/images/screenshots/aks.png new file mode 100755 index 000000000..fd568bf6a Binary files /dev/null and b/static/images/screenshots/aks.png differ diff --git a/static/images/screenshots/debezium-connect-cluster.png b/static/images/screenshots/debezium-connect-cluster.png new file mode 100755 index 000000000..e7a1f4d86 Binary files /dev/null and b/static/images/screenshots/debezium-connect-cluster.png differ diff --git a/static/images/screenshots/load-to-df.png b/static/images/screenshots/load-to-df.png new file mode 100755 index 000000000..0bbe9c544 Binary files /dev/null and b/static/images/screenshots/load-to-df.png differ diff --git a/static/images/screenshots/python.png b/static/images/screenshots/python.png new file mode 100755 index 000000000..119f5ea3a Binary files /dev/null and b/static/images/screenshots/python.png differ diff --git a/static/images/screenshots/scala-solution.png b/static/images/screenshots/scala-solution.png new file mode 100755 index 000000000..0adbbbb36 Binary files /dev/null and b/static/images/screenshots/scala-solution.png differ diff --git a/static/images/service/background-pattern copy.svg b/static/images/service/background-pattern copy.svg new file mode 100755 index 000000000..ed66856c0 --- /dev/null +++ b/static/images/service/background-pattern copy.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/service/background-pattern.svg b/static/images/service/background-pattern.svg new file mode 100755 index 000000000..be26757a7 --- /dev/null +++ b/static/images/service/background-pattern.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/service/background-shape.svg b/static/images/service/background-shape.svg new file mode 100755 index 000000000..33cd235cd --- /dev/null +++ b/static/images/service/background-shape.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/static/images/service/branding.svg b/static/images/service/branding.svg new file mode 100755 index 000000000..52ece1d7f --- /dev/null +++ b/static/images/service/branding.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/images/service/creative.svg b/static/images/service/creative.svg new file mode 100755 index 000000000..ec038caa8 --- /dev/null +++ b/static/images/service/creative.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/static/images/service/ui-ux.svg b/static/images/service/ui-ux.svg new file mode 100755 index 000000000..71a689fa1 --- /dev/null +++ b/static/images/service/ui-ux.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/service/web.svg b/static/images/service/web.svg new file mode 100755 index 000000000..cda4a4d41 --- /dev/null +++ b/static/images/service/web.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/static/images/single-blog/aksdbz.png b/static/images/single-blog/aksdbz.png new file mode 100755 index 000000000..b8eba3e13 Binary files /dev/null and b/static/images/single-blog/aksdbz.png differ diff --git a/static/images/single-blog/blog-img.jpg b/static/images/single-blog/blog-img.jpg new file mode 100755 index 000000000..69daf74f0 Binary files /dev/null and b/static/images/single-blog/blog-img.jpg differ diff --git a/static/images/single-blog/data-lakehouse.png b/static/images/single-blog/data-lakehouse.png new file mode 100755 index 000000000..56b286d99 Binary files /dev/null and b/static/images/single-blog/data-lakehouse.png differ diff --git a/static/images/single-blog/feature-image.jpg b/static/images/single-blog/feature-image.jpg new file mode 100755 index 000000000..db07a6598 Binary files /dev/null and b/static/images/single-blog/feature-image.jpg differ diff --git a/static/images/single-blog/fireworks.gif b/static/images/single-blog/fireworks.gif new file mode 100755 index 000000000..f4352adf0 Binary files /dev/null and b/static/images/single-blog/fireworks.gif differ diff --git a/static/images/single-blog/fy22/1flipfest.webp b/static/images/single-blog/fy22/1flipfest.webp new file mode 100755 index 000000000..d7b493c01 Binary files /dev/null and b/static/images/single-blog/fy22/1flipfest.webp differ diff --git a/static/images/single-blog/fy22/colchuck-winter3.webp b/static/images/single-blog/fy22/colchuck-winter3.webp new file mode 100755 index 000000000..be3259832 Binary files /dev/null and b/static/images/single-blog/fy22/colchuck-winter3.webp differ diff --git a/static/images/single-blog/fy22/colchuck.webp b/static/images/single-blog/fy22/colchuck.webp new file mode 100755 index 000000000..bd574932f Binary files /dev/null and b/static/images/single-blog/fy22/colchuck.webp differ diff --git a/static/images/single-blog/fy22/google-cloud.webp b/static/images/single-blog/fy22/google-cloud.webp new file mode 100755 index 000000000..48fcd69cb Binary files /dev/null and b/static/images/single-blog/fy22/google-cloud.webp differ diff --git a/static/images/single-blog/fy22/lake22.webp b/static/images/single-blog/fy22/lake22.webp new file mode 100755 index 000000000..6b3b10529 Binary files /dev/null and b/static/images/single-blog/fy22/lake22.webp differ diff --git a/static/images/single-blog/fy22/painting.webp b/static/images/single-blog/fy22/painting.webp new file mode 100755 index 000000000..e1cda551a Binary files /dev/null and b/static/images/single-blog/fy22/painting.webp differ diff --git a/static/images/single-blog/fy22/paradise.webp b/static/images/single-blog/fy22/paradise.webp new file mode 100755 index 000000000..b170e0d5e Binary files /dev/null and b/static/images/single-blog/fy22/paradise.webp differ diff --git a/static/images/single-blog/fy22/rainier-paradise.webp b/static/images/single-blog/fy22/rainier-paradise.webp new file mode 100755 index 000000000..02e2ab22e Binary files /dev/null and b/static/images/single-blog/fy22/rainier-paradise.webp differ diff --git a/static/images/single-blog/fy22/rainier-sunrise.webp b/static/images/single-blog/fy22/rainier-sunrise.webp new file mode 100755 index 000000000..0fa415c16 Binary files /dev/null and b/static/images/single-blog/fy22/rainier-sunrise.webp differ diff --git a/static/images/single-blog/fy22/seattle-by-night.webp b/static/images/single-blog/fy22/seattle-by-night.webp new file mode 100755 index 000000000..cd20e1b05 Binary files /dev/null and b/static/images/single-blog/fy22/seattle-by-night.webp differ diff --git a/static/images/single-blog/fy22/skiing.webp b/static/images/single-blog/fy22/skiing.webp new file mode 100755 index 000000000..5242965f1 Binary files /dev/null and b/static/images/single-blog/fy22/skiing.webp differ diff --git a/static/images/single-blog/fy22/talk.webp b/static/images/single-blog/fy22/talk.webp new file mode 100755 index 000000000..e29025214 Binary files /dev/null and b/static/images/single-blog/fy22/talk.webp differ diff --git a/static/images/single-blog/fy22/talk2.webp b/static/images/single-blog/fy22/talk2.webp new file mode 100755 index 000000000..e609f32ff Binary files /dev/null and b/static/images/single-blog/fy22/talk2.webp differ diff --git a/static/images/single-blog/fy22/team-outing.webp b/static/images/single-blog/fy22/team-outing.webp new file mode 100755 index 000000000..4bebab33b Binary files /dev/null and b/static/images/single-blog/fy22/team-outing.webp differ diff --git a/static/images/single-blog/fy22/treehouse.webp b/static/images/single-blog/fy22/treehouse.webp new file mode 100755 index 000000000..070bad328 Binary files /dev/null and b/static/images/single-blog/fy22/treehouse.webp differ diff --git a/static/images/single-blog/fy22/winter-hike.webp b/static/images/single-blog/fy22/winter-hike.webp new file mode 100755 index 000000000..5f7ee1ddc Binary files /dev/null and b/static/images/single-blog/fy22/winter-hike.webp differ diff --git a/static/images/single-blog/git-reset.png b/static/images/single-blog/git-reset.png new file mode 100755 index 000000000..cce8d6958 Binary files /dev/null and b/static/images/single-blog/git-reset.png differ diff --git a/static/images/single-blog/microsoft.jpg b/static/images/single-blog/microsoft.jpg new file mode 100755 index 000000000..bc9b6e737 Binary files /dev/null and b/static/images/single-blog/microsoft.jpg differ diff --git a/static/images/single-blog/pyspark-synapse.png b/static/images/single-blog/pyspark-synapse.png new file mode 100755 index 000000000..693053923 Binary files /dev/null and b/static/images/single-blog/pyspark-synapse.png differ diff --git a/static/images/single-blog/pyspark-synapse.webp b/static/images/single-blog/pyspark-synapse.webp new file mode 100755 index 000000000..6d546a368 Binary files /dev/null and b/static/images/single-blog/pyspark-synapse.webp differ diff --git a/static/images/single-blog/query-performance.jpeg b/static/images/single-blog/query-performance.jpeg new file mode 100755 index 000000000..a4266f83b Binary files /dev/null and b/static/images/single-blog/query-performance.jpeg differ diff --git a/static/images/single-blog/sql-pool.jpeg b/static/images/single-blog/sql-pool.jpeg new file mode 100755 index 000000000..986529ac8 Binary files /dev/null and b/static/images/single-blog/sql-pool.jpeg differ diff --git a/static/images/site-navigation/logo.png b/static/images/site-navigation/logo.png new file mode 100755 index 000000000..1c96b7afd Binary files /dev/null and b/static/images/site-navigation/logo.png differ diff --git a/static/images/site-navigation/logonew.png b/static/images/site-navigation/logonew.png new file mode 100755 index 000000000..735174630 Binary files /dev/null and b/static/images/site-navigation/logonew.png differ diff --git a/static/images/skill/myskillupdate.png b/static/images/skill/myskillupdate.png new file mode 100755 index 000000000..0aaff665a Binary files /dev/null and b/static/images/skill/myskillupdate.png differ diff --git a/static/images/skill/skill-background-shape.svg b/static/images/skill/skill-background-shape.svg new file mode 100755 index 000000000..d9c945851 --- /dev/null +++ b/static/images/skill/skill-background-shape.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/static/images/skill/skill-mask-svg.svg b/static/images/skill/skill-mask-svg.svg new file mode 100755 index 000000000..71af2d707 --- /dev/null +++ b/static/images/skill/skill-mask-svg.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/images/skill/skill_image.jpg b/static/images/skill/skill_image.jpg new file mode 100755 index 000000000..a5e1e3315 Binary files /dev/null and b/static/images/skill/skill_image.jpg differ diff --git a/static/images/skill/skills.png b/static/images/skill/skills.png new file mode 100755 index 000000000..3dc6d06bd Binary files /dev/null and b/static/images/skill/skills.png differ diff --git a/static/img/Canadian Data Guy-logos.jpeg b/static/img/Canadian Data Guy-logos.jpeg new file mode 100644 index 000000000..6558ee25d Binary files /dev/null and b/static/img/Canadian Data Guy-logos.jpeg differ diff --git a/static/img/Canadian Data Guy-logos_black.png b/static/img/Canadian Data Guy-logos_black.png new file mode 100644 index 000000000..c340b1279 Binary files /dev/null and b/static/img/Canadian Data Guy-logos_black.png differ diff --git a/static/img/Canadian Data Guy-logos_transparent.png b/static/img/Canadian Data Guy-logos_transparent.png new file mode 100644 index 000000000..3cd18df1e Binary files /dev/null and b/static/img/Canadian Data Guy-logos_transparent.png differ diff --git a/static/img/Canadian Data Guy-logos_white.png b/static/img/Canadian Data Guy-logos_white.png new file mode 100644 index 000000000..f5c2814c2 Binary files /dev/null and b/static/img/Canadian Data Guy-logos_white.png differ diff --git a/static/img/apple-touch-icon-old.png b/static/img/apple-touch-icon-old.png new file mode 100644 index 000000000..b3531dd4d Binary files /dev/null and b/static/img/apple-touch-icon-old.png differ diff --git a/static/img/apple-touch-icon.png b/static/img/apple-touch-icon.png index b3531dd4d..0e46869d4 100644 Binary files a/static/img/apple-touch-icon.png and b/static/img/apple-touch-icon.png differ diff --git a/static/img/avatar.png b/static/img/avatar.png new file mode 100644 index 000000000..7bea86165 Binary files /dev/null and b/static/img/avatar.png differ diff --git a/static/img/cdg5.png b/static/img/cdg5.png new file mode 100644 index 000000000..16f59d744 Binary files /dev/null and b/static/img/cdg5.png differ diff --git a/static/img/cdg9.png b/static/img/cdg9.png new file mode 100644 index 000000000..9302d6e76 Binary files /dev/null and b/static/img/cdg9.png differ diff --git a/static/img/chicago.png b/static/img/chicago.png new file mode 100644 index 000000000..7bea86165 Binary files /dev/null and b/static/img/chicago.png differ diff --git a/static/img/cpg3.png b/static/img/cpg3.png new file mode 100644 index 000000000..e69a42c82 Binary files /dev/null and b/static/img/cpg3.png differ diff --git a/static/img/favicon-old.ico b/static/img/favicon-old.ico new file mode 100644 index 000000000..01970e8f3 Binary files /dev/null and b/static/img/favicon-old.ico differ diff --git a/static/img/favicon.ico b/static/img/favicon.ico index 01970e8f3..b162d6770 100644 Binary files a/static/img/favicon.ico and b/static/img/favicon.ico differ diff --git a/static/img/mp.png b/static/img/mp.png new file mode 100644 index 000000000..841a28f1f Binary files /dev/null and b/static/img/mp.png differ diff --git a/static/img/skills-small.jpg b/static/img/skills-small.jpg new file mode 100644 index 000000000..ce0c08bcf Binary files /dev/null and b/static/img/skills-small.jpg differ diff --git a/static/img/soni.jpg b/static/img/soni.jpg new file mode 100644 index 000000000..e6d6eefe9 Binary files /dev/null and b/static/img/soni.jpg differ diff --git a/storage.rules b/storage.rules new file mode 100644 index 000000000..9f33d22cb --- /dev/null +++ b/storage.rules @@ -0,0 +1,8 @@ +rules_version = '2'; +service firebase.storage { + match /b/{bucket}/o { + match /{allPaths=**} { + allow read, write: if false; + } + } +}