From da7e4514cfdbe52e8f1ef6aadd54c401e2b849f5 Mon Sep 17 00:00:00 2001 From: Ruben Beltran del Rio Date: Wed, 17 Sep 2025 15:59:30 +0200 Subject: Use zola --- .build.yml | 9 +- .gitignore | 1 + config.toml | 16 + content/_index.md | 66 ++++ content/impressum/_index.md | 15 + content/privacy-policy/_index.md | 45 +++ css/style.css | 356 ------------------ font/LibreCaslonCondensed-Bold.woff2 | Bin 50324 -> 0 bytes font/LibreCaslonCondensed-BoldItalic.woff2 | Bin 55408 -> 0 bytes font/LibreCaslonCondensed-Italic.woff2 | Bin 54768 -> 0 bytes font/LibreCaslonCondensed-Italic[wght].woff2 | Bin 69292 -> 0 bytes font/LibreCaslonCondensed-Medium.woff2 | Bin 51460 -> 0 bytes font/LibreCaslonCondensed-MediumItalic.woff2 | Bin 57656 -> 0 bytes font/LibreCaslonCondensed-Regular.woff2 | Bin 50380 -> 0 bytes font/LibreCaslonCondensed-SemiBold.woff2 | Bin 51788 -> 0 bytes font/LibreCaslonCondensed-SemiBoldItalic.woff2 | Bin 57696 -> 0 bytes font/LibreCaslonCondensed[wdth,wght].woff2 | Bin 62484 -> 0 bytes font/LibreCaslonCondensed[wght].woff2 | Bin 62552 -> 0 bytes font/Ronzino-Bold.woff2 | Bin 36908 -> 0 bytes font/Ronzino-BoldOblique.woff2 | Bin 36260 -> 0 bytes font/Ronzino-Medium.woff2 | Bin 36748 -> 0 bytes font/Ronzino-MediumOblique.woff2 | Bin 36304 -> 0 bytes font/Ronzino-Oblique.woff2 | Bin 36260 -> 0 bytes font/Ronzino-Regular.woff2 | Bin 36316 -> 0 bytes humans.txt | 8 - img/selfie-export.png | Bin 1390 -> 0 bytes img/selfie.png | Bin 1390 -> 0 bytes index.html | 108 ------ js/animation.js | 49 --- static/css/style.css | 402 +++++++++++++++++++++ static/font/LibreCaslonCondensed-Bold.woff2 | Bin 0 -> 50324 bytes static/font/LibreCaslonCondensed-BoldItalic.woff2 | Bin 0 -> 55408 bytes static/font/LibreCaslonCondensed-Italic.woff2 | Bin 0 -> 54768 bytes .../font/LibreCaslonCondensed-Italic[wght].woff2 | Bin 0 -> 69292 bytes static/font/LibreCaslonCondensed-Medium.woff2 | Bin 0 -> 51460 bytes .../font/LibreCaslonCondensed-MediumItalic.woff2 | Bin 0 -> 57656 bytes static/font/LibreCaslonCondensed-Regular.woff2 | Bin 0 -> 50380 bytes static/font/LibreCaslonCondensed-SemiBold.woff2 | Bin 0 -> 51788 bytes .../font/LibreCaslonCondensed-SemiBoldItalic.woff2 | Bin 0 -> 57696 bytes static/font/LibreCaslonCondensed[wdth,wght].woff2 | Bin 0 -> 62484 bytes static/font/LibreCaslonCondensed[wght].woff2 | Bin 0 -> 62552 bytes static/font/Ronzino-Bold.woff2 | Bin 0 -> 36908 bytes static/font/Ronzino-BoldOblique.woff2 | Bin 0 -> 36260 bytes static/font/Ronzino-Medium.woff2 | Bin 0 -> 36748 bytes static/font/Ronzino-MediumOblique.woff2 | Bin 0 -> 36304 bytes static/font/Ronzino-Oblique.woff2 | Bin 0 -> 36260 bytes static/font/Ronzino-Regular.woff2 | Bin 0 -> 36316 bytes static/humans.txt | 8 + static/img/selfie-export.png | Bin 0 -> 1390 bytes static/img/selfie.png | Bin 0 -> 1390 bytes static/js/animation.js | 49 +++ templates/base.html | 48 +++ templates/index.html | 5 + templates/shortcodes/window.html | 8 + templates/text.html | 8 + 55 files changed, 679 insertions(+), 522 deletions(-) create mode 100644 config.toml create mode 100644 content/_index.md create mode 100644 content/impressum/_index.md create mode 100644 content/privacy-policy/_index.md delete mode 100644 css/style.css delete mode 100755 font/LibreCaslonCondensed-Bold.woff2 delete mode 100755 font/LibreCaslonCondensed-BoldItalic.woff2 delete mode 100755 font/LibreCaslonCondensed-Italic.woff2 delete mode 100755 font/LibreCaslonCondensed-Italic[wght].woff2 delete mode 100755 font/LibreCaslonCondensed-Medium.woff2 delete mode 100755 font/LibreCaslonCondensed-MediumItalic.woff2 delete mode 100755 font/LibreCaslonCondensed-Regular.woff2 delete mode 100755 font/LibreCaslonCondensed-SemiBold.woff2 delete mode 100755 font/LibreCaslonCondensed-SemiBoldItalic.woff2 delete mode 100755 font/LibreCaslonCondensed[wdth,wght].woff2 delete mode 100755 font/LibreCaslonCondensed[wght].woff2 delete mode 100644 font/Ronzino-Bold.woff2 delete mode 100644 font/Ronzino-BoldOblique.woff2 delete mode 100644 font/Ronzino-Medium.woff2 delete mode 100644 font/Ronzino-MediumOblique.woff2 delete mode 100644 font/Ronzino-Oblique.woff2 delete mode 100644 font/Ronzino-Regular.woff2 delete mode 100644 humans.txt delete mode 100644 img/selfie-export.png delete mode 100644 img/selfie.png delete mode 100644 index.html delete mode 100644 js/animation.js create mode 100644 static/css/style.css create mode 100755 static/font/LibreCaslonCondensed-Bold.woff2 create mode 100755 static/font/LibreCaslonCondensed-BoldItalic.woff2 create mode 100755 static/font/LibreCaslonCondensed-Italic.woff2 create mode 100755 static/font/LibreCaslonCondensed-Italic[wght].woff2 create mode 100755 static/font/LibreCaslonCondensed-Medium.woff2 create mode 100755 static/font/LibreCaslonCondensed-MediumItalic.woff2 create mode 100755 static/font/LibreCaslonCondensed-Regular.woff2 create mode 100755 static/font/LibreCaslonCondensed-SemiBold.woff2 create mode 100755 static/font/LibreCaslonCondensed-SemiBoldItalic.woff2 create mode 100755 static/font/LibreCaslonCondensed[wdth,wght].woff2 create mode 100755 static/font/LibreCaslonCondensed[wght].woff2 create mode 100644 static/font/Ronzino-Bold.woff2 create mode 100644 static/font/Ronzino-BoldOblique.woff2 create mode 100644 static/font/Ronzino-Medium.woff2 create mode 100644 static/font/Ronzino-MediumOblique.woff2 create mode 100644 static/font/Ronzino-Oblique.woff2 create mode 100644 static/font/Ronzino-Regular.woff2 create mode 100644 static/humans.txt create mode 100644 static/img/selfie-export.png create mode 100644 static/img/selfie.png create mode 100644 static/js/animation.js create mode 100644 templates/base.html create mode 100644 templates/index.html create mode 100644 templates/shortcodes/window.html create mode 100644 templates/text.html diff --git a/.build.yml b/.build.yml index 97d4094..500d281 100644 --- a/.build.yml +++ b/.build.yml @@ -9,5 +9,12 @@ secrets: - a1e0e3da-c1ad-473a-ad69-8878f267a628 - 0b0d3e5e-fbdc-41d0-97ed-ee654fe797ff tasks: + - install_zola: | + rustup default stable + cargo install zola + - generate: | + cd ruben.bdr.sh + zola build - sync: | - rsync -r --exclude '.build.yml' --exclude '.git' ruben.bdr.sh/ deploy@conchos.unlimited.pizza:/srv/http/ruben.bdr.sh + cd ruben.bdr.sh/public + rsync -r . deploy@conchos.unlimited.pizza:/srv/http/ruben.bdr.sh diff --git a/.gitignore b/.gitignore index e43b0f9..592a6ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +public diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..739f807 --- /dev/null +++ b/config.toml @@ -0,0 +1,16 @@ +# The URL the site will be built for +base_url = "https://ruben.bdr.sh" + +# Whether to automatically compile all Sass files in the sass directory +compile_sass = false + +# Whether to build a search index to be used later on by a JavaScript library +build_search_index = true + +[markdown] +# Whether to do syntax highlighting +# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola +highlight_code = false + +[extra] +# Put all your custom variables here diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..2cd776e --- /dev/null +++ b/content/_index.md @@ -0,0 +1,66 @@ ++++ +title = "Holistic Tech Services" +description = "Rubén Beltrán del Río is a freelance software engineer that offers a wide-range of services, including native iOS and macOS development, web development, backend services, infrastructure, as well as organizational and leadership consulting for tech teams." ++++ +
+ {% window(title="Welcome.") %} + A pixel-art portrait of Rubén doing a peace sign and sticking his tongue out +

Howdy! I'm Rubén, a freelance full-stack software engineer based in Berlin. Looking for some help with your project? I might be able to help!

+ {% end %} +
+
+
+

I can help you build.

+
    +
  • Websites that are fast, accessible and search engine optimized.
  • +
  • Native apps for iOS, iPadOS or macOS.
  • +
  • Backend Services with great observability, clear documentation, and made to scale.
  • +
  • Data Pipelines from tracking planning, to data warehouse extract-load-transform pipelines that give you accurate and actionable information.
  • +
  • Infrastructure that can tie all your services, documented as code.
  • +
+
+
+

Optimize and improve.

+
    +
  • Supply chain analysis to reduce risk from dependencies across different packaging ecosystems.
  • +
  • Automate code quality and deployment.
  • +
  • Reduce cloud cost by optimizing your resource usage, plans and overall architecture.
  • +
  • Optimize Large-Language-Model usage by creating custom tooling and Model Context Protocol (MCP) servers.
  • +
+
+
+

Or help your team go fast.

+
    +
  • AI Transformation: Teach your organization how to work with AI, and build the right tools to support it.
  • +
  • Upskill Your Team: Bring your team up to speed with new technologies, architecture patterns or processes.
  • +
  • Product Engineering Mindset: Bring product and engineering closer together.
  • +
  • Experimentation mindset, help your team learn how to design, execute, and interpret experiments.
  • +
  • Systems Analysis: Streamline your operations by understanding how your team, resources and processes come together, finding chokepoints, and reducing waste.
  • +
+
+
+
+
+

Ain't that right!

+

With over 25 years of experience, I've worked with big and small teams, ad-supported and sales-driven products, bootstrapped startups looking to launch, and established companies looking to scale up. I've worked with all sorts of stacks:

+
    +
  • Languages: JavaScript, Rust, Ruby, Python, PHP, Swift, Objective-C.
  • +
  • Cloud Providers: AWS, Scaleway, Cloudflare, Google Cloud, Azure.
  • +
  • CI Systems: GitHub, Gitlab, CircleCI, TravisCI, Jenkins, Bitrise.
  • +
  • Database Systems: PostgreSQL, MySQL/Aurora, Redis, MongoDB, Apache Cassandra, Snowflake.
  • +
  • Analytics Software: Google Analytics, Amplitude, PostHog, .
  • +
+
+ {% window(title="Get in touch.") %} +

You can book a call to tell me about your project face to face. Or you if you prefer text, you can send me an e-mail!

+ + + {% end %} +
+
+
+

But don't take my word for it.

+

Here's what some lovely people have to say about me.

+
+
+ diff --git a/content/impressum/_index.md b/content/impressum/_index.md new file mode 100644 index 0000000..465b4d8 --- /dev/null +++ b/content/impressum/_index.md @@ -0,0 +1,15 @@ ++++ +title = "Holistic Tech Services &mdot; Impressum" +description = "Rubén Beltrán del Río is a freelance software engineer that offers a wide-range of services, including native iOS and macOS development, web development, backend services, infrastructure, as well as organizational and leadership consulting for tech teams." +template = "text.html" ++++ +## Impressum. + +All the content and services in this page are provided by: + +**Rubén Fabián Beltrán del Río Vara**
+Ueckermünder Str. 5
+10439 Berlin
+[inquiries@work.bdr.sh](hello@work.bdr.sh) + +You can send any inquiries to the aforementioned e-mail address. diff --git a/content/privacy-policy/_index.md b/content/privacy-policy/_index.md new file mode 100644 index 0000000..fe35188 --- /dev/null +++ b/content/privacy-policy/_index.md @@ -0,0 +1,45 @@ ++++ +title = "Holistic Tech Services &mdot; Privacy Policy" +description = "Rubén Beltrán del Río is a freelance software engineer that offers a wide-range of services, including native iOS and macOS development, web development, backend services, infrastructure, as well as organizational and leadership consulting for tech teams." +template = "text.html" ++++ +## Privacy Policy + +In order to offer my services, I need to collect some data. This policy shows the entirety of our data collection, and how long we keep it for. + +### The What, When, Why and How long of our Data Collection. + +#### When you visit this website. +* **What**: Our server collects IP addresses and browser User-Agents but never links it to your identity. +* **Why**: We use this data to understand our traffic and block potential malicious actors (eg. Bots) +* **For how long**: 30 days. + +#### When you send me an e-mail or book an appointment. +* **What**: Your contact information and the content of the messages. +* **Why**: In order to respond to your inquiries. +* **How Long**: 2 years if no business relationship develops, 6 years for business correspondence. +* **Lawful basis**: Legitimate interests. + +#### When we work on a project together. +* **What**: Contact details, business information, invoicing data, project communications. +* **Why**: This is necessary to fulfill the contract, and for my tax obligations. +* **For how long**: Business records are kept for 6 years after project completion, financial records for 10 years after final invoice. +* **Lawful basis**: Contract performance and legal obligation. + +### Data Security +Your data is protected using appropriate technical and organizational measures, including firewalls, encrypted data transmission, and secure server infrastructure. + +### Data Sharing +Your data is never shared with third parties, except as required by law (such as tax authorities or in response to valid legal requests). + +### Your Rights +You can request access to, correction of, deletion of, or object to processing of your data (subject to legal retention requirements). + +Contact me at [dataprotection@work.bdr.sh](mailto:dataprotection@work.bdr.sh) for data protection queries. I will respond to your requests within one month. + +### Data Controller +Rubén Fabián Beltrán del Río Vara +[dataprotection@work.bdr.sh](mailto:dataprotection@work.bdr.sh) + +### Governing Law +This privacy policy and all data processing activities are governed by German law and the European General Data Protection Regulation (GDPR). diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 795a205..0000000 --- a/css/style.css +++ /dev/null @@ -1,356 +0,0 @@ -/* Font Faces */ -@font-face { - font-family: 'Libre Caslon'; - src: url('/font/LibreCaslonCondensed[wdth,wght].woff2') format('woff2-variations'); - font-weight: 400 700; - font-stretch: 75% 100%; - font-style: normal; - font-display: swap; -} - -@font-face { - font-family: 'Libre Caslon'; - src: url('/font/LibreCaslonCondensed-Italic[wght].woff2') format('woff2-variations'); - font-weight: 400 700; - font-style: italic; - font-display: swap; -} - -/* Theme */ -:root { - --primary-color: #0000f5; - --background: #fff; - --foreground: #000019; - --foreground-emphasis: #191919; - --selection-color: #ccc; - --font-display: 'Libre Caslon', 'Times New Roman', 'Georgia', serif; - --font-body: 'Helvetica Neue', Helvetica, Arial, sans-serif; - --root-font-size: 1rem; - --base-grid: 1.25rem; - --radius: 4px; - --border-width: 1px; - --outer-radius: calc(var(--radius) - var(--border-width)); -} - -::selection { - background-color: var(--selection-color); - -webkit-text-stroke: 0; - color: var(--primary-color); -} - -/* CSS Reset */ -* {margin: 0; padding: 0; box-sizing: border-box} -html { scroll-behavior: smooth } - -/* Base Elements */ -h1,h2,h3,h4 { font-weight: 300 } -li,p { - hyphens: auto; - text-align: justify; - transform: translateY(4px); -} -strong { - color: var(--foreground-emphasis); - font-weight: 700; -} -p { margin-bottom: var(--base-grid); } -a[role="button"], -button { - border: var(--border-width) solid var(--foreground); - border-radius: var(--radius); - background-color: var(--background); - color: var(--foreground); - cursor: pointer; - font-family: var(--font-body); - font-size: 1rem; - line-height: 1.25; - font-weight: 700; - display: inline-block; - text-decoration: none; - transition: 0.1s all linear; - padding: calc(0.5lh - var(--border-width)) 1lh; - margin-bottom: 1lh; - - &:active { - background-color: var(--foreground); - color: var(--background); - } - - label { - line-height: 1.25; - cursor: pointer; - } -} - -.textured { - h2, p, ul { - -webkit-text-stroke: calc(4 * var(--border-width)) var(--background); - paint-order: stroke fill; - } -} - -/* The Contents */ -body { - font-family: var(--font-body); - font-synthesis: none; - color: var(--foreground); - background: var(--primary-color); - font-size: var(--root-font-size); - line-height: 1.25; - display: flex; - flex-direction: column; - align-items: stretch; - height: 100vh; - - -/* Header */ - - & > header { - color: #fff; - display: flex; - background: var(--primary-color); - font-family: var(--font-display); - padding: var(--base-grid); - position: relative; - - h1 { - font-size: 2rem; - flex: 0 0 auto; - line-height: 1.25; - - @media (max-width: 600px) { - font-size: 1rem; - line-height: 1.25; - } - } - - p { - font-size: 4.5rem; - font-style: italic; - line-height: 5rem; - margin: 0.5lh auto 0.25lh; - transform: translateY(-5px); - white-space: pre; - width: min-content; - - @media (max-width: 800px) { - font-size: 3rem; - line-height: 3.75rem; - margin: 1lh auto var(--base-grid); - transform: translateY(-7px) - } - - @media (max-width: 600px) { - font-size: 2rem; - line-height: 1.25; - margin: 0.75lh auto 0.25lh; - transform: translateY(-1px) - } - - @media (max-width: 360px) { - font-size: 1rem; - margin: 2lh auto 0; - transform: translateY(4px); - } - } - - a { - color: #fff; - display: block; - margin-top: 9px; - text-decoration: none; - width: min-content; - word-spacing: 100vw; - - @media (max-width: 600px) { - margin-top: 4px - } - } - - /* Animation */ - - canvas { - background: var(--primary-color); - display: block; - height: 60px; - width: 60px; - - @media (max-width: 600px) { - height: 40px; - width: 40px; - } - } - } -} -/* The Main Body */ - -main { - flex-grow: 1; - background-color: var(--background); - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: center; - - h2 { - font-size: 1.5rem; - line-height: 1.6667; - font-family: var(--font-display); - font-weight: 500; - transform: translateY(-8px); - } - - ul { - list-style: none; - margin-left: 0; - } - li { - position: relative; - padding-left: var(--base-grid); - - &::before { - content: "✳"; - width: var(--base-grid); - display: inline-block; - margin-left: calc(-1 * var(--base-grid)); - text-align: left; - } - } -} - -#introduction { - - padding: calc(1 * var(--base-grid)); - - .content { - column-gap: var(--base-grid); - width: 100%; - max-width: calc(35 * var(--base-grid)); - background: var(--background); - display: flex; - justify-content: center; - align-items: center; - } -} - -#services { - column-gap: calc(2 * var(--base-grid)); - row-gap: calc(3 * var(--base-grid)); - padding: calc(2 * var(--base-grid)); - width: 100%; - max-width: calc(83 * var(--base-grid)); - background: var(--background); - display: flex; - justify-content: center; - flex-wrap: wrap; - - section { - flex: 1; - min-width: calc(17 * var(--base-grid)); - max-width: calc(25 * var(--base-grid)); - - @media (max-width: 360px) { - min-width: calc(100vw - 2 * var(--base-grid)); - } - } - -} - -#skills { - background-image: url('data:image/svg+xml;utf8,'); - padding: calc(2 * var(--base-grid)); - width: 100%; - display: flex; - column-gap: calc(4 * var(--base-grid)); - row-gap: calc(3 * var(--base-grid)); - flex-wrap: wrap; - - @media (max-width: 360px) { - padding: calc(2 * var(--base-grid)) var(--base-grid); - } - - section { - flex: 1; - min-width: calc(17 * var(--base-grid)); - - @media (max-width: 360px) { - min-width: calc(100vw - 3 * var(--base-grid)); - } - } -} - -#testimonials { - background-image: url('data:image/svg+xml;utf8,'); - padding: calc(2 * var(--base-grid)); - width: 100%; - display: flex; - column-gap: calc(4 * var(--base-grid)); - row-gap: calc(3 * var(--base-grid)); - flex-wrap: wrap; - - @media (max-width: 360px) { - padding: calc(2 * var(--base-grid)) var(--base-grid); - } - - section { - flex: 1; - min-width: calc(17 * var(--base-grid)); - - @media (max-width: 360px) { - min-width: calc(100vw - 3 * var(--base-grid)); - } - } -} - -/* The Floating Window */ -.cutoff-corners { - background-color: #fff; - border: var(--border-width) solid #000; - border-radius: var(--radius); -} - -.window { - - .titlebar { - border-bottom: var(--border-width) solid #000; - padding: calc(3 * var(--border-width)) calc(8 * var(--border-width)) calc(4 * var(--border-width)) ; - line-height: calc(9 * var(--border-width)); - text-align: center; - font-weight: 700; - background: repeating-linear-gradient( - 0deg, - black, - black var(--border-width), - white var(--border-width), - white calc(var(--border-width) * 2) - ); - background-clip: content-box; - background-origin: content-box; - list-style: none; - - &::marker { - content: ""; - } - - .label { - background-color: #fff; - padding: var(--border-width) calc(5 * var(--border-width)); - line-height: calc(12 * var(--border-width)); - height: calc(12 * var(--border-width)); - display: inline-block; - } - } - - .content { - padding: calc(var(--base-grid) - 1px) var(--base-grid); - } - - hr.divider { - margin-left: -6px; - margin-right: -6px; - } -} - -aside { - background: var(--background) -} diff --git a/font/LibreCaslonCondensed-Bold.woff2 b/font/LibreCaslonCondensed-Bold.woff2 deleted file mode 100755 index 4a4a681..0000000 Binary files a/font/LibreCaslonCondensed-Bold.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-BoldItalic.woff2 b/font/LibreCaslonCondensed-BoldItalic.woff2 deleted file mode 100755 index 944988e..0000000 Binary files a/font/LibreCaslonCondensed-BoldItalic.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-Italic.woff2 b/font/LibreCaslonCondensed-Italic.woff2 deleted file mode 100755 index 6b37d8a..0000000 Binary files a/font/LibreCaslonCondensed-Italic.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-Italic[wght].woff2 b/font/LibreCaslonCondensed-Italic[wght].woff2 deleted file mode 100755 index 0431f04..0000000 Binary files a/font/LibreCaslonCondensed-Italic[wght].woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-Medium.woff2 b/font/LibreCaslonCondensed-Medium.woff2 deleted file mode 100755 index 22bc644..0000000 Binary files a/font/LibreCaslonCondensed-Medium.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-MediumItalic.woff2 b/font/LibreCaslonCondensed-MediumItalic.woff2 deleted file mode 100755 index dddc7ff..0000000 Binary files a/font/LibreCaslonCondensed-MediumItalic.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-Regular.woff2 b/font/LibreCaslonCondensed-Regular.woff2 deleted file mode 100755 index da1778e..0000000 Binary files a/font/LibreCaslonCondensed-Regular.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-SemiBold.woff2 b/font/LibreCaslonCondensed-SemiBold.woff2 deleted file mode 100755 index 99396d6..0000000 Binary files a/font/LibreCaslonCondensed-SemiBold.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed-SemiBoldItalic.woff2 b/font/LibreCaslonCondensed-SemiBoldItalic.woff2 deleted file mode 100755 index 9b8a820..0000000 Binary files a/font/LibreCaslonCondensed-SemiBoldItalic.woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed[wdth,wght].woff2 b/font/LibreCaslonCondensed[wdth,wght].woff2 deleted file mode 100755 index d280f8a..0000000 Binary files a/font/LibreCaslonCondensed[wdth,wght].woff2 and /dev/null differ diff --git a/font/LibreCaslonCondensed[wght].woff2 b/font/LibreCaslonCondensed[wght].woff2 deleted file mode 100755 index 67fff68..0000000 Binary files a/font/LibreCaslonCondensed[wght].woff2 and /dev/null differ diff --git a/font/Ronzino-Bold.woff2 b/font/Ronzino-Bold.woff2 deleted file mode 100644 index 440957c..0000000 Binary files a/font/Ronzino-Bold.woff2 and /dev/null differ diff --git a/font/Ronzino-BoldOblique.woff2 b/font/Ronzino-BoldOblique.woff2 deleted file mode 100644 index 29038fa..0000000 Binary files a/font/Ronzino-BoldOblique.woff2 and /dev/null differ diff --git a/font/Ronzino-Medium.woff2 b/font/Ronzino-Medium.woff2 deleted file mode 100644 index 6161f9c..0000000 Binary files a/font/Ronzino-Medium.woff2 and /dev/null differ diff --git a/font/Ronzino-MediumOblique.woff2 b/font/Ronzino-MediumOblique.woff2 deleted file mode 100644 index 0879928..0000000 Binary files a/font/Ronzino-MediumOblique.woff2 and /dev/null differ diff --git a/font/Ronzino-Oblique.woff2 b/font/Ronzino-Oblique.woff2 deleted file mode 100644 index 0b4e1ac..0000000 Binary files a/font/Ronzino-Oblique.woff2 and /dev/null differ diff --git a/font/Ronzino-Regular.woff2 b/font/Ronzino-Regular.woff2 deleted file mode 100644 index b8763ad..0000000 Binary files a/font/Ronzino-Regular.woff2 and /dev/null differ diff --git a/humans.txt b/humans.txt deleted file mode 100644 index b2bdf36..0000000 --- a/humans.txt +++ /dev/null @@ -1,8 +0,0 @@ -/* TEAM */ -Maintainer: Rubén Beltrán del Río -Site: contact@r.bdr.sh -Mastodon: https://friendship.quest/@ruben -From: Berlin, Germany - -/* SITE */ -Language: English diff --git a/img/selfie-export.png b/img/selfie-export.png deleted file mode 100644 index d50ef8f..0000000 Binary files a/img/selfie-export.png and /dev/null differ diff --git a/img/selfie.png b/img/selfie.png deleted file mode 100644 index d50ef8f..0000000 Binary files a/img/selfie.png and /dev/null differ diff --git a/index.html b/index.html deleted file mode 100644 index 652cf13..0000000 --- a/index.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - Rubén Beltrán del Río — Holistic Tech Services - - - - - - - - - - - - -
-

- - Rubén Beltrán del Río -

-

✷ Holistic - tech - services ✷

-
-
-
-
-
- Welcome. -
-
- A pixel-art portrait of Rubén doing a peace sign and sticking his tongue out -

Howdy! I'm Rubén, a freelance full-stack software engineer based in Berlin. Looking for some help with your project? I might be able to help!

-
-
-
-
-
-

I can help you build.

-
    -
  • Websites that are fast, accessible and search engine optimized.
  • -
  • Native apps for iOS, iPadOS or macOS.
  • -
  • Backend Services with great observability, clear documentation, and made to scale.
  • -
  • Data Pipelines from tracking planning, to data warehouse extract-load-transform pipelines that give you accurate and actionable information.
  • -
  • Infrastructure that can tie all your services, documented as code.
  • -
-
-
-

Optimize and improve.

-
    -
  • Supply chain analysis to reduce risk from dependencies across different packaging ecosystems.
  • -
  • Automate code quality and deployment.
  • -
  • Reduce cloud cost by optimizing your resource usage, plans and overall architecture.
  • -
  • Optimize Large-Language-Model usage by creating custom tooling and Model Context Protocol (MCP) servers.
  • -
-
-
-

Or help your team go fast.

-
    -
  • AI Transformation: Teach your organization how to work with AI, and build the right tools to support it.
  • -
  • Upskill Your Team: Bring your team up to speed with new technologies, architecture patterns or processes.
  • -
  • Product Engineering Mindset: Bring product and engineering closer together.
  • -
  • Experimentation mindset, help your team learn how to design, execute, and interpret experiments.
  • -
  • Systems Analysis: Streamline your operations by understanding how your team, resources and processes come together, finding chokepoints, and reducing waste.
  • -
-
-
-
-
-

Ain't that right!

-

With over 25 years of experience, I've worked with big and small teams, ad-supported and sales-driven products, bootstrapped startups looking to launch, and established companies looking to scale up. I've worked with all sorts of stacks:

-
    -
  • Languages: JavaScript, Rust, Ruby, Python, PHP, Swift, Objective-C.
  • -
  • Cloud Providers: AWS, Scaleway, Cloudflare, Google Cloud, Azure.
  • -
  • CI Systems: GitHub, Gitlab, CircleCI, TravisCI, Jenkins, Bitrise.
  • -
  • Database Systems: PostgreSQL, MySQL/Aurora, Redis, MongoDB, Apache Cassandra, Snowflake.
  • -
  • Analytics Software: Google Analytics, Amplitude, PostHog, .
  • -
-
-
-
- Get in touch. -
-
-

You can book a call to tell me about your project face to face. Or you if you prefer text, you can send me an e-mail!

- - -
-
-
-
-
-

But don't take my word for it.

-

Here's what some lovely people have to say about me.

-
-
-
- - diff --git a/js/animation.js b/js/animation.js deleted file mode 100644 index d78ac8f..0000000 --- a/js/animation.js +++ /dev/null @@ -1,49 +0,0 @@ -var K=document.querySelector('canvas'), -{random:z,PI,cos,sin}=Math, -X=K.getContext('2d'),F=24,l=-1001, -u=0,S=Array(~~(z()*2)+3).fill().map(()=>({ - C:~~(z()*10)+4, - r:30-4*u++, T:z()*.1, - R:z()>.25?z()/5-.1:0, - S:z()>.25?z()*.5+.5:1, - n:30,t:0,d:1 -})),Z=matchMedia('(prefers-reduced-motion: reduce)').matches?0:1 - -X.strokeStyle='#fff' -X.lineWidth=1 -function pl(x,y,r,s,t) { - X.beginPath() - for (let i=0; i<=s; i++) { - var a=(i*2*PI/s)+t, -px=x+r*cos(a), py=y+r*sin(a) - i==0?X.moveTo(px, py):X.lineTo(px, py) - } -X.stroke() -} -function d(s) { - var cx=30,cy=30,{T,d,r,S,n}=s - s.n+=T*d - if (s.n>=r) { - s.n=r - s.d=-1 - } - if (s.n<=r*S) { - s.n=r*S - s.d=1 - } - s.t+=s.R - s.C<13?pl(cx,cy,n,s.C,s.t):(X.beginPath(),X.arc(cx,cy,n,0,2*PI),X.stroke()) -} -function f(t) { - if(Z)requestAnimationFrame(f) - var dt=t-l - if (dt>1000/F) { - X.clearRect(0,0,60,60) - X.rect(0,0,60,60) - X.stroke() - S.map(d) - l=t - } -} -K.onclick=()=>((Z=!Z)&&f()) -f(0) diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..7eeb4e0 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,402 @@ +/* Font Faces */ +@font-face { + font-family: 'Libre Caslon'; + src: url('/font/LibreCaslonCondensed[wdth,wght].woff2') format('woff2-variations'); + font-weight: 400 700; + font-stretch: 75% 100%; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Libre Caslon'; + src: url('/font/LibreCaslonCondensed-Italic[wght].woff2') format('woff2-variations'); + font-weight: 400 700; + font-style: italic; + font-display: swap; +} + +/* Theme */ +:root { + --primary-color: #0000f5; + --background: #fff; + --foreground: #000019; + --foreground-emphasis: #191919; + --selection-color: #ccc; + --font-display: 'Libre Caslon', 'Times New Roman', 'Georgia', serif; + --font-body: 'Helvetica Neue', Helvetica, Arial, sans-serif; + --root-font-size: 1rem; + --base-grid: 1.25rem; + --radius: 4px; + --border-width: 1px; + --outer-radius: calc(var(--radius) - var(--border-width)); +} + +::selection { + background-color: var(--selection-color); + -webkit-text-stroke: 0; + color: var(--primary-color); +} + +/* CSS Reset */ +* {margin: 0; padding: 0; box-sizing: border-box} +html { scroll-behavior: smooth } + +/* Base Elements */ +h1,h2,h3,h4 { font-weight: 300 } +li,p { + hyphens: auto; + text-align: justify; + transform: translateY(4px); +} +strong { + color: var(--foreground-emphasis); + font-weight: 700; +} +p { margin-bottom: var(--base-grid); } +a[role="button"], +button { + border: var(--border-width) solid var(--foreground); + border-radius: var(--radius); + background-color: var(--background); + color: var(--foreground); + cursor: pointer; + font-family: var(--font-body); + font-size: 1rem; + line-height: 1.25; + font-weight: 700; + display: inline-block; + text-decoration: none; + transition: 0.1s all linear; + padding: calc(0.5lh - var(--border-width)) 1lh; + margin-bottom: 1lh; + + &:active { + background-color: var(--foreground); + color: var(--background); + } + + label { + line-height: 1.25; + cursor: pointer; + } +} + +.textured { + h2, p, ul { + -webkit-text-stroke: calc(4 * var(--border-width)) var(--background); + paint-order: stroke fill; + } +} + +/* The Contents */ +body { + font-family: var(--font-body); + font-synthesis: none; + color: var(--foreground); + background: var(--primary-color); + font-size: var(--root-font-size); + line-height: 1.25; + display: flex; + flex-direction: column; + align-items: stretch; + height: 100vh; + + +/* Header */ + + & > header { + color: #fff; + display: flex; + background: var(--primary-color); + font-family: var(--font-display); + padding: var(--base-grid); + position: relative; + + h1 { + font-size: 2rem; + flex: 0 0 auto; + line-height: 1.25; + + @media (max-width: 600px) { + font-size: 1rem; + line-height: 1.25; + } + } + + p { + font-size: 4.5rem; + font-style: italic; + line-height: 5rem; + margin: 0.5lh auto 0.25lh; + transform: translateY(-5px); + white-space: pre; + width: min-content; + + @media (max-width: 800px) { + font-size: 3rem; + line-height: 3.75rem; + margin: 1lh auto var(--base-grid); + transform: translateY(-7px) + } + + @media (max-width: 600px) { + font-size: 2rem; + line-height: 1.25; + margin: 0.75lh auto 0.25lh; + transform: translateY(-1px) + } + + @media (max-width: 360px) { + font-size: 1rem; + margin: 2lh auto 0; + transform: translateY(4px); + } + } + + a { + color: #fff; + display: block; + margin-top: 9px; + text-decoration: none; + width: min-content; + word-spacing: 100vw; + + @media (max-width: 600px) { + margin-top: 0; + transform: translateY(4px); + } + } + + /* Animation */ + + canvas { + background: var(--primary-color); + display: block; + height: 60px; + width: 60px; + + @media (max-width: 600px) { + height: 40px; + width: 40px; + } + } + } +} +/* The Main Body */ + +main { + flex-grow: 1; + background-color: var(--background); + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + + h2 { + font-size: 1.5rem; + line-height: 1.6667; + font-family: var(--font-display); + font-weight: 500; + transform: translateY(-8px); + } + + h3 { + font-size: 1.25rem; + line-height: 1; + font-weight: 500; + transform: translateY(2px); + margin: 2lh 0 1lh; + } + + h4 { + font-weight: 500; + font-size: 1rem; + line-height: 1.25; + transform: translateY(4px); + margin: 1lh 0; + } + + ul { + list-style: none; + margin-left: 0; + margin-bottom: var(--base-grid); + } + li { + position: relative; + padding-left: var(--base-grid); + + &::before { + content: "✳"; + width: var(--base-grid); + display: inline-block; + margin-left: calc(-1 * var(--base-grid)); + text-align: left; + } + } +} + +#introduction { + + padding: var(--base-grid); + + .content { + column-gap: var(--base-grid); + width: 100%; + max-width: calc(35 * var(--base-grid)); + background: var(--background); + display: flex; + justify-content: center; + align-items: center; + } +} + +#services { + column-gap: calc(2 * var(--base-grid)); + row-gap: calc(3 * var(--base-grid)); + padding: calc(2 * var(--base-grid)); + width: 100%; + max-width: calc(83 * var(--base-grid)); + background: var(--background); + display: flex; + justify-content: center; + flex-wrap: wrap; + + section { + flex: 1; + min-width: calc(17 * var(--base-grid)); + max-width: calc(25 * var(--base-grid)); + + ul { + margin-bottom: 0; + } + + @media (max-width: 360px) { + min-width: calc(100vw - 2 * var(--base-grid)); + } + } + +} + +#skills { + background-image: url('data:image/svg+xml;utf8,'); + padding: calc(2 * var(--base-grid)); + width: 100%; + display: flex; + column-gap: calc(4 * var(--base-grid)); + row-gap: calc(3 * var(--base-grid)); + flex-wrap: wrap; + + @media (max-width: 360px) { + padding: calc(2 * var(--base-grid)) var(--base-grid); + } + + section { + flex: 1; + min-width: calc(17 * var(--base-grid)); + + @media (max-width: 360px) { + min-width: calc(100vw - 3 * var(--base-grid)); + } + } +} + +#testimonials { + background-image: url('data:image/svg+xml;utf8,'); + padding: calc(2 * var(--base-grid)); + width: 100%; + display: flex; + column-gap: calc(4 * var(--base-grid)); + row-gap: calc(3 * var(--base-grid)); + flex-wrap: wrap; + + @media (max-width: 360px) { + padding: calc(2 * var(--base-grid)) var(--base-grid); + } + + section { + flex: 1; + min-width: calc(17 * var(--base-grid)); + + @media (max-width: 360px) { + min-width: calc(100vw - 3 * var(--base-grid)); + } + } +} + +footer { + + color: #fff; + padding: calc(2 * var(--base-grid)); + + ul { + list-style-position: inside; + } + li { + padding: 0 var(--base-grid); + list-style-position: inside; + float: left; + } + + a { + color: #fff; + } +} + +#text { + max-width: calc(35 * var(--base-grid)); + padding: var(--base-grid); +} + +/* The Floating Window */ +.cutoff-corners { + background-color: #fff; + border: var(--border-width) solid #000; + border-radius: var(--radius); +} + +.window { + + .titlebar { + border-bottom: var(--border-width) solid #000; + padding: calc(3 * var(--border-width)) calc(8 * var(--border-width)) calc(4 * var(--border-width)) ; + line-height: calc(9 * var(--border-width)); + text-align: center; + font-weight: 700; + background: repeating-linear-gradient( + 0deg, + black, + black var(--border-width), + white var(--border-width), + white calc(var(--border-width) * 2) + ); + background-clip: content-box; + background-origin: content-box; + list-style: none; + + &::marker { + content: ""; + } + + .label { + background-color: #fff; + padding: var(--border-width) calc(5 * var(--border-width)); + line-height: calc(12 * var(--border-width)); + height: calc(12 * var(--border-width)); + display: inline-block; + } + } + + .content { + padding: calc(var(--base-grid) - 1px) var(--base-grid); + } + + hr.divider { + margin-left: -6px; + margin-right: -6px; + } +} + +aside { + background: var(--background) +} diff --git a/static/font/LibreCaslonCondensed-Bold.woff2 b/static/font/LibreCaslonCondensed-Bold.woff2 new file mode 100755 index 0000000..4a4a681 Binary files /dev/null and b/static/font/LibreCaslonCondensed-Bold.woff2 differ diff --git a/static/font/LibreCaslonCondensed-BoldItalic.woff2 b/static/font/LibreCaslonCondensed-BoldItalic.woff2 new file mode 100755 index 0000000..944988e Binary files /dev/null and b/static/font/LibreCaslonCondensed-BoldItalic.woff2 differ diff --git a/static/font/LibreCaslonCondensed-Italic.woff2 b/static/font/LibreCaslonCondensed-Italic.woff2 new file mode 100755 index 0000000..6b37d8a Binary files /dev/null and b/static/font/LibreCaslonCondensed-Italic.woff2 differ diff --git a/static/font/LibreCaslonCondensed-Italic[wght].woff2 b/static/font/LibreCaslonCondensed-Italic[wght].woff2 new file mode 100755 index 0000000..0431f04 Binary files /dev/null and b/static/font/LibreCaslonCondensed-Italic[wght].woff2 differ diff --git a/static/font/LibreCaslonCondensed-Medium.woff2 b/static/font/LibreCaslonCondensed-Medium.woff2 new file mode 100755 index 0000000..22bc644 Binary files /dev/null and b/static/font/LibreCaslonCondensed-Medium.woff2 differ diff --git a/static/font/LibreCaslonCondensed-MediumItalic.woff2 b/static/font/LibreCaslonCondensed-MediumItalic.woff2 new file mode 100755 index 0000000..dddc7ff Binary files /dev/null and b/static/font/LibreCaslonCondensed-MediumItalic.woff2 differ diff --git a/static/font/LibreCaslonCondensed-Regular.woff2 b/static/font/LibreCaslonCondensed-Regular.woff2 new file mode 100755 index 0000000..da1778e Binary files /dev/null and b/static/font/LibreCaslonCondensed-Regular.woff2 differ diff --git a/static/font/LibreCaslonCondensed-SemiBold.woff2 b/static/font/LibreCaslonCondensed-SemiBold.woff2 new file mode 100755 index 0000000..99396d6 Binary files /dev/null and b/static/font/LibreCaslonCondensed-SemiBold.woff2 differ diff --git a/static/font/LibreCaslonCondensed-SemiBoldItalic.woff2 b/static/font/LibreCaslonCondensed-SemiBoldItalic.woff2 new file mode 100755 index 0000000..9b8a820 Binary files /dev/null and b/static/font/LibreCaslonCondensed-SemiBoldItalic.woff2 differ diff --git a/static/font/LibreCaslonCondensed[wdth,wght].woff2 b/static/font/LibreCaslonCondensed[wdth,wght].woff2 new file mode 100755 index 0000000..d280f8a Binary files /dev/null and b/static/font/LibreCaslonCondensed[wdth,wght].woff2 differ diff --git a/static/font/LibreCaslonCondensed[wght].woff2 b/static/font/LibreCaslonCondensed[wght].woff2 new file mode 100755 index 0000000..67fff68 Binary files /dev/null and b/static/font/LibreCaslonCondensed[wght].woff2 differ diff --git a/static/font/Ronzino-Bold.woff2 b/static/font/Ronzino-Bold.woff2 new file mode 100644 index 0000000..440957c Binary files /dev/null and b/static/font/Ronzino-Bold.woff2 differ diff --git a/static/font/Ronzino-BoldOblique.woff2 b/static/font/Ronzino-BoldOblique.woff2 new file mode 100644 index 0000000..29038fa Binary files /dev/null and b/static/font/Ronzino-BoldOblique.woff2 differ diff --git a/static/font/Ronzino-Medium.woff2 b/static/font/Ronzino-Medium.woff2 new file mode 100644 index 0000000..6161f9c Binary files /dev/null and b/static/font/Ronzino-Medium.woff2 differ diff --git a/static/font/Ronzino-MediumOblique.woff2 b/static/font/Ronzino-MediumOblique.woff2 new file mode 100644 index 0000000..0879928 Binary files /dev/null and b/static/font/Ronzino-MediumOblique.woff2 differ diff --git a/static/font/Ronzino-Oblique.woff2 b/static/font/Ronzino-Oblique.woff2 new file mode 100644 index 0000000..0b4e1ac Binary files /dev/null and b/static/font/Ronzino-Oblique.woff2 differ diff --git a/static/font/Ronzino-Regular.woff2 b/static/font/Ronzino-Regular.woff2 new file mode 100644 index 0000000..b8763ad Binary files /dev/null and b/static/font/Ronzino-Regular.woff2 differ diff --git a/static/humans.txt b/static/humans.txt new file mode 100644 index 0000000..b2bdf36 --- /dev/null +++ b/static/humans.txt @@ -0,0 +1,8 @@ +/* TEAM */ +Maintainer: Rubén Beltrán del Río +Site: contact@r.bdr.sh +Mastodon: https://friendship.quest/@ruben +From: Berlin, Germany + +/* SITE */ +Language: English diff --git a/static/img/selfie-export.png b/static/img/selfie-export.png new file mode 100644 index 0000000..d50ef8f Binary files /dev/null and b/static/img/selfie-export.png differ diff --git a/static/img/selfie.png b/static/img/selfie.png new file mode 100644 index 0000000..d50ef8f Binary files /dev/null and b/static/img/selfie.png differ diff --git a/static/js/animation.js b/static/js/animation.js new file mode 100644 index 0000000..d78ac8f --- /dev/null +++ b/static/js/animation.js @@ -0,0 +1,49 @@ +var K=document.querySelector('canvas'), +{random:z,PI,cos,sin}=Math, +X=K.getContext('2d'),F=24,l=-1001, +u=0,S=Array(~~(z()*2)+3).fill().map(()=>({ + C:~~(z()*10)+4, + r:30-4*u++, T:z()*.1, + R:z()>.25?z()/5-.1:0, + S:z()>.25?z()*.5+.5:1, + n:30,t:0,d:1 +})),Z=matchMedia('(prefers-reduced-motion: reduce)').matches?0:1 + +X.strokeStyle='#fff' +X.lineWidth=1 +function pl(x,y,r,s,t) { + X.beginPath() + for (let i=0; i<=s; i++) { + var a=(i*2*PI/s)+t, +px=x+r*cos(a), py=y+r*sin(a) + i==0?X.moveTo(px, py):X.lineTo(px, py) + } +X.stroke() +} +function d(s) { + var cx=30,cy=30,{T,d,r,S,n}=s + s.n+=T*d + if (s.n>=r) { + s.n=r + s.d=-1 + } + if (s.n<=r*S) { + s.n=r*S + s.d=1 + } + s.t+=s.R + s.C<13?pl(cx,cy,n,s.C,s.t):(X.beginPath(),X.arc(cx,cy,n,0,2*PI),X.stroke()) +} +function f(t) { + if(Z)requestAnimationFrame(f) + var dt=t-l + if (dt>1000/F) { + X.clearRect(0,0,60,60) + X.rect(0,0,60,60) + X.stroke() + S.map(d) + l=t + } +} +K.onclick=()=>((Z=!Z)&&f()) +f(0) diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..04120ba --- /dev/null +++ b/templates/base.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + Rubén Beltrán del Río — {{ section.title }} + + + + + + + + + + + + +
+

+ + Rubén Beltrán del Río +

+

✷ Holistic + tech + services ✷

+
+
+ {% block content %}{% endblock %} +
+ + + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..be0db07 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block content %} +{{ section.content | safe }} +{% endblock %} diff --git a/templates/shortcodes/window.html b/templates/shortcodes/window.html new file mode 100644 index 0000000..3fe8b50 --- /dev/null +++ b/templates/shortcodes/window.html @@ -0,0 +1,8 @@ +
+
+ {{title}} +
+
+ {{ body | safe }} +
+
diff --git a/templates/text.html b/templates/text.html new file mode 100644 index 0000000..9ba2a71 --- /dev/null +++ b/templates/text.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +
+ + {{ section.content | safe }} +
+{% endblock %} -- cgit