{"id":19,"date":"2026-10-25T17:07:04","date_gmt":"2026-10-25T14:07:04","guid":{"rendered":"https:\/\/qcoders.com\/home\/"},"modified":"2026-11-02T09:00:53","modified_gmt":"2026-11-02T07:00:53","slug":"home","status":"publish","type":"page","link":"https:\/\/qcoders.com\/ar\/","title":{"rendered":"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"19\" class=\"elementor elementor-19\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5f53131b e-con-full elementor-hidden-desktop elementor-hidden-tablet e-flex e-con e-parent\" data-id=\"5f53131b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1133fc2c elementor-widget elementor-widget-html\" data-id=\"1133fc2c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<style>\n@keyframes qcmh-drift{from{transform:rotateY(0) translateY(0)}to{transform:rotateY(-5deg) translateY(-6px)}}\n@keyframes qcmh-rise{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}\n@keyframes qcmh-pulse{50%{box-shadow:0 0 0 7px rgba(200,245,59,0)}}\n.qcm{\n  --ink:#0A0E17; --step:#131A27; --line:#232C3D; --paper:#F6F5F0; --mute:#A9AEB8; --volt:#C8F53B;\n  --pad:20px;\n  --sans:\"Readex Pro\",system-ui,sans-serif; --mono:\"JetBrains Mono\",ui-monospace,monospace;\n}\n.qcm *{box-sizing:border-box;margin:0;padding:0}\n.qcm{-webkit-text-size-adjust:100%;scroll-behavior:smooth}\n.qcm{background:var(--ink);color:var(--paper);font-family:var(--sans);font-weight:400;line-height:1.45;overflow-x:hidden;-webkit-font-smoothing:antialiased}\n.qcm img{display:block;max-width:100%}\n.qcm a{color:inherit;text-decoration:none}\n.qcm .label{font-family:var(--mono);font-size:12px;letter-spacing:.02em;color:var(--volt);font-weight:500}\n.qcm .label.dim{color:var(--mute)}\n.qcm \/* the hero's label and the stage's code tags are drawn from their data attributes: the same look,.qcm but not\n   read as the page's first lines,.qcm so the line under the h1 is the introduction *\/\n.label[data-l]::before{content:attr(data-l)}\n.qcm .tag[data-tag]::after{content:attr(data-tag)}\n.qcm \/* header *\/\n.top{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;height:60px;padding:0 var(--pad);background:rgba(10,14,23,.86);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid rgba(246,245,240,.06)}\n.qcm .top svg{height:22px;width:auto}\n.qcm .pill{display:inline-flex;align-items:center;gap:8px;height:38px;padding:0 14px;border-radius:99px;border:1px solid rgba(246,245,240,.18);font-size:14px;font-weight:500}\n.qcm .pill i{width:8px;height:8px;border-radius:50%;background:var(--volt);box-shadow:0 0 0 4px rgba(200,245,59,.18)}\n.qcm \/* hero *\/\n.hero{position:relative;padding:22px var(--pad) 0;overflow:hidden}\n.qcm .hero h1{font-weight:700;font-size:clamp(46px,14.6vw,60px);line-height:.94;letter-spacing:-.035em;margin-top:12px}\n.qcm .hero h1 span{display:block}\n.qcm .hero h1 em{font-style:normal;color:var(--volt)}\n.qcm .stage{position:relative;height:318px;margin:6px calc(var(--pad) * -1) 0;perspective:1100px}\n.qcm .stack{position:absolute;inset:0;transform-style:preserve-3d}\n.qcm .scr{position:absolute;border-radius:14px;overflow:hidden;background:var(--step);box-shadow:0 30px 60px -20px rgba(0,0,0,.7),0 0 0 1px rgba(246,245,240,.08)}\n.qcm .scr img{width:100%;height:100%;object-fit:cover;object-position:top left}\n.qcm .scr.desk{width:430px;height:269px;left:-6px;top:40px;transform:rotateX(18deg) rotateY(-26deg) rotateZ(4deg) translateZ(-60px);opacity:.92}\n.qcm .scr.ph{width:150px;height:325px;border-radius:22px;border:5px solid #1c2433}\n.qcm .scr.ph1{right:112px;top:14px;transform:rotateY(-18deg) rotateZ(-7deg) translateZ(20px)}\n.qcm .scr.ph2{right:-4px;top:-2px;transform:rotateY(-14deg) rotateZ(6deg) translateZ(60px)}\n.qcm .stage::after{content:\"\";position:absolute;left:0;right:0;bottom:0;height:90px;background:linear-gradient(to bottom,rgba(10,14,23,0),var(--ink));pointer-events:none}\n.qcm .tag{position:absolute;z-index:3;font-family:var(--mono);font-size:11px;background:var(--ink);border:1px solid rgba(200,245,59,.5);color:var(--volt);padding:5px 9px;border-radius:6px}\n.qcm .tag.t1{left:16px;bottom:118px}\n.qcm .tag.t2{right:14px;bottom:44px}\n.qcm \/* volt block *\/\n.volt{background:var(--volt);color:var(--ink);padding:22px var(--pad) 22px;position:relative;z-index:2}\n.qcm .volt p{font-size:19px;line-height:1.3;font-weight:500;letter-spacing:-.01em;max-width:340px}\n.qcm .volt .label{color:var(--ink);opacity:.75;display:block;margin-bottom:8px}\n.qcm .btns{display:flex;gap:10px;margin-top:18px}\n.qcm .btn{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;height:52px;border-radius:12px;font-weight:600;font-size:16px}\n.qcm .btn.ink{background:var(--ink);color:var(--paper)}\n.qcm .btn.line{border:1.5px solid var(--ink);color:var(--ink);flex:0 0 auto;padding:0 18px}\n.qcm .btn svg{width:20px;height:20px}\n.qcm \/* section heads *\/\n.sh{padding:56px var(--pad) 22px;display:flex;justify-content:space-between;align-items:flex-end;gap:12px}\n.qcm .sh h2{font-size:38px;line-height:1;letter-spacing:-.03em;font-weight:700;margin-top:10px}\n.qcm .sh .count{font-family:var(--mono);font-size:12px;color:var(--mute);white-space:nowrap}\n.qcm \/* project tiles *\/\n.tiles{display:flex;flex-direction:column;gap:14px}\n.qcm .tile{display:block;position:relative;background:var(--step);overflow:hidden}\n.qcm .tile .pic{position:relative;aspect-ratio:16\/10;overflow:hidden;perspective:900px}\n.qcm .tile .pic img{width:100%;height:100%;object-fit:cover;transition:transform .6s cubic-bezier(.2,.7,.2,1)}\n.qcm .tile .meta{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:18px var(--pad) 20px}\n.qcm \/* a project card: the heading is the product's name (label face),.qcm the line under it a paragraph (large) *\/\n.tile h3.label::before{content:\"\/\/ \"}\n.qcm .tile .tt{font-size:24px;line-height:1.1;letter-spacing:-.02em;font-weight:600;margin-top:6px;color:var(--paper)}\n.qcm .go{flex:0 0 44px;width:44px;height:44px;border-radius:50%;background:var(--volt);color:var(--ink);display:grid;place-items:center;margin-top:4px}\n.qcm .go svg{width:20px;height:20px}\n.qcm .tile .num{position:absolute;top:12px;left:12px;z-index:2;font-family:var(--mono);font-size:11px;background:rgba(10,14,23,.82);color:var(--paper);padding:5px 8px;border-radius:6px}\n.qcm .tile.flip .meta{flex-direction:row-reverse}\n.qcm .tile.flip .meta>div{text-align:right}\n.qcm .tile.flip .go{background:transparent;border:1.5px solid var(--volt);color:var(--volt)}\n.qcm .tile:active .pic img{transform:scale(1.03)}\n.qcm .more{display:flex;align-items:center;justify-content:space-between;margin:14px var(--pad) 0;height:56px;padding:0 18px;border:1px solid var(--line);border-radius:12px;font-weight:500}\n.qcm .more svg{width:20px;height:20px;color:var(--volt)}\n.qcm \/* promise band *\/\n.band{background:var(--volt);color:var(--ink);margin-top:56px;padding:40px var(--pad) 36px;position:relative;overflow:hidden}\n.qcm .band .label{color:var(--ink);opacity:.75}\n.qcm .band h2{font-size:40px;line-height:1;letter-spacing:-.035em;font-weight:700;margin-top:12px;color:var(--ink)}\n.qcm .band p{font-size:17px;line-height:1.4;margin-top:14px;max-width:330px;font-weight:400}\n.qcm .facts{display:grid;grid-template-columns:1fr 1fr 1fr;margin-top:24px;border-top:1.5px solid var(--ink)}\n.qcm .facts div{padding:12px 0 0;font-size:14px;line-height:1.25;font-weight:500}\n.qcm .facts div+div{padding-left:12px;border-left:1.5px solid rgba(10,14,23,.25)}\n.qcm .facts b{display:block;font-size:22px;font-weight:700;letter-spacing:-.02em}\n.qcm .band .who{font-size:15px;line-height:1.45;margin-top:22px;max-width:330px;font-weight:500}\n.qcm .band .who a{text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;white-space:nowrap}\n.qcm .sh .intro{font-size:16px;color:var(--mute);margin-top:14px;max-width:350px}\n.qcm .sh.sh-block{display:block}\n.qcm \/* services *\/\n.svc{list-style:none;border-top:1px solid var(--line)}\n.qcm .svc a{display:grid;grid-template-columns:34px 1fr 24px;align-items:center;gap:8px;padding:18px var(--pad);border-bottom:1px solid var(--line)}\n.qcm .svc .n{font-family:var(--mono);font-size:12px;color:var(--volt);align-self:start;padding-top:12px}\n.qcm .svc strong{display:block;font-size:34px;line-height:1.02;letter-spacing:-.03em;font-weight:600}\n.qcm .svc small{display:block;font-size:14px;color:var(--mute);margin-top:6px;line-height:1.35}\n.qcm .svc svg{width:22px;height:22px;color:var(--mute)}\n.qcm .svc a:active strong{color:var(--volt)}\n.qcm \/* the four steps (the phone pages' rows) *\/\n.rows{list-style:none;border-top:1px solid var(--line)}\n.qcm .rows>li{display:grid;grid-template-columns:34px 1fr;gap:8px;padding:22px var(--pad) 24px;border-bottom:1px solid var(--line)}\n.qcm .rows .n{font-family:var(--mono);font-size:12px;color:var(--volt);padding-top:9px}\n.qcm .rows h3{font-size:28px;line-height:1.05;letter-spacing:-.025em;font-weight:600}\n.qcm .rows p{font-size:15px;color:var(--mute);margin-top:8px;line-height:1.45}\n.qcm .rows .you{color:var(--paper);font-size:16px;line-height:1.4;font-weight:500}\n.qcm \/* the need chips of the close (the phone pages' close) *\/\n.needs-q{margin-top:24px;display:block}\n.qcm .needs{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;list-style:none}\n.qcm .needs a{display:inline-flex;align-items:center;min-height:42px;padding:0 15px;border:1px solid rgba(200,245,59,.45);border-radius:99px;font-size:15px;font-weight:500}\n.qcm .needs a:active{background:var(--volt);color:var(--ink)}\n.qcm \/* CTA *\/\n.cta{position:relative;padding:60px var(--pad) 40px;overflow:hidden}\n.qcm .cta h2{font-size:clamp(40px,12vw,48px);line-height:.94;letter-spacing:-.04em;font-weight:700;margin-top:12px}\n.qcm .cta h2 em{font-style:normal;color:var(--volt)}\n.qcm .cta .sub{font-size:17px;color:var(--mute);margin-top:16px;max-width:330px}\n.qcm .wa{display:flex;align-items:center;gap:14px;margin-top:26px;background:var(--volt);color:var(--ink);border-radius:18px;padding:18px 18px}\n.qcm .wa .ic{flex:0 0 52px;height:52px;border-radius:14px;background:var(--ink);color:var(--volt);display:grid;place-items:center}\n.qcm .wa .ic svg{width:28px;height:28px}\n.qcm .wa b{display:block;font-size:20px;font-weight:700;letter-spacing:-.01em;line-height:1.1}\n.qcm .wa span{display:block;font-size:15px;font-weight:500;margin-top:3px}\n.qcm .wa .arr{margin-left:auto;width:22px;height:22px;flex:0 0 22px}\n.qcm .alt{display:flex;justify-content:space-between;align-items:center;margin-top:14px;padding:16px 18px;border:1px solid var(--line);border-radius:14px}\n.qcm .alt b{font-weight:500}\n.qcm .langs{display:flex;gap:8px;margin-top:22px;flex-wrap:wrap}\n.qcm .langs span{font-size:14px;padding:7px 12px;border:1px solid var(--line);border-radius:99px;color:var(--paper)}\n.qcm footer{padding:26px var(--pad) 34px;border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}\n.qcm footer svg{height:18px;width:auto}\n.qcm footer small{font-family:var(--mono);font-size:11px;color:var(--mute)}\n@media (prefers-reduced-motion:no-preference){\n.qcm .stack{animation:qcmh-drift 9s ease-in-out infinite alternate}\n.qcm.js .rv{opacity:0;transform:translateY(22px);transition:opacity .7s ease,transform .7s cubic-bezier(.2,.7,.2,1)}\n.qcm.js .rv.in{opacity:1;transform:none}\n.qcm .hero h1 span{animation:qcmh-rise .8s cubic-bezier(.2,.7,.2,1) both}\n.qcm .hero h1 span:nth-child(2){animation-delay:.08s}\n.qcm .hero h1 span:nth-child(3){animation-delay:.16s}\n.qcm .pill i{animation:qcmh-pulse 2.4s ease-in-out infinite}\n}\n<\/style>\n<div class=\"qcm\">\n\n<svg width=\"0\" height=\"0\" style=\"position:absolute\" aria-hidden=\"true\">\n  \n  <symbol id=\"arr\" viewBox=\"0 0 24 24\"><path d=\"M5 12h14M13 6l6 6-6 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/symbol>\n  <symbol id=\"arr-up\" viewBox=\"0 0 24 24\"><path d=\"M7 17 17 7M8 7h9v9\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/symbol>\n  <symbol id=\"wa\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12.04 2a9.9 9.9 0 0 0-8.5 15l-1.4 5 5.2-1.36A9.9 9.9 0 1 0 12.04 2Zm0 18.1a8.2 8.2 0 0 1-4.2-1.15l-.3-.18-3.08.8.82-3-.2-.31a8.2 8.2 0 1 1 6.96 3.84Zm4.5-6.14c-.25-.12-1.46-.72-1.69-.8-.23-.08-.39-.12-.55.12-.17.25-.64.8-.78.97-.14.16-.29.18-.53.06a6.7 6.7 0 0 1-3.37-2.94c-.25-.44.25-.41.72-1.36.08-.16.04-.3-.02-.43l-.76-1.83c-.2-.48-.4-.41-.55-.42h-.47a.9.9 0 0 0-.65.3 2.74 2.74 0 0 0-.85 2.04c0 1.2.87 2.37 1 2.53.12.16 1.72 2.63 4.17 3.69 1.55.67 2.16.73 2.93.61.47-.07 1.46-.6 1.66-1.17.2-.58.2-1.07.15-1.17-.07-.1-.23-.16-.47-.28Z\"\/><\/symbol>\n<\/svg>\n\n\n\n\n\n<section class=\"hero\">\n  <span class=\"label\" data-l=\"\/\/ Software that works for you\"><\/span>\n  <h1><span>Code that<\/span> <span>runs your<\/span> <span><em>business.<\/em><\/span><\/h1>\n  <div class=\"stage\" aria-hidden=\"true\">\n    <div class=\"stack\">\n      <div class=\"scr desk\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp 1440w\" sizes=\"430px\" alt=\"Sales CRM: the deal pipeline board\" width=\"1440\" height=\"900\" fetchpriority=\"high\"><\/div>\n      <div class=\"scr ph ph1\"><img decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-route-list.webp\" alt=\"Delivery app: today's route on the driver's phone\" width=\"390\" height=\"844\"><\/div>\n      <div class=\"scr ph ph2\"><img decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-mobile-product.webp\" alt=\"Fashion online store: a product page on a phone\" width=\"390\" height=\"844\"><\/div>\n    <\/div>\n    <span class=\"tag t1\" data-tag=\"\/\/ sales-crm\"><\/span>\n    <span class=\"tag t2\" data-tag=\"\/\/ online-store\"><\/span>\n  <\/div>\n<\/section>\n\n<section class=\"volt\">\n  <span class=\"label\" data-l=\"\/\/ What we build\"><\/span>\n  <p>Custom software for your business: websites, online stores, apps, business systems and AI. For any size, anywhere.<\/p>\n  <div class=\"btns\">\n    <a class=\"btn ink\" href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.\" target=\"_blank\" rel=\"noopener\"><svg><use href=\"#wa\"\/><\/svg>WhatsApp us<\/a>\n    <a class=\"btn line\" href=\"#qcm-work\">See work<\/a>\n  <\/div>\n<\/section>\n\n\n<section id=\"qcm-work\">\n  <div class=\"sh\">\n    <div><span class=\"label\">\/\/ Our work<\/span><h2>Built and<br>running.<\/h2><\/div>\n    <span class=\"count\">06 projects<\/span>\n  <\/div>\n  <div class=\"tiles\">\n    <a class=\"tile rv\" href=\"\/work\/sales-crm\/\">\n      <div class=\"pic\"><span class=\"num\">01 \/ Systems<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"Sales CRM deals pipeline screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">Sales CRM<\/h3> <p class=\"tt\">Every lead, call and WhatsApp chat in one place.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n    <a class=\"tile flip rv\" href=\"\/work\/online-store\/\">\n      <div class=\"pic\"><span class=\"num\">02 \/ Stores<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"Fashion online store home page and mobile checkout screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">Fashion online store<\/h3> <p class=\"tt\">A modern fashion store, with one admin to ship orders.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n    <a class=\"tile rv\" href=\"\/work\/delivery-app\/\">\n      <div class=\"pic\"><span class=\"num\">03 \/ Apps<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"Delivery app dispatch board screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">Delivery app<\/h3> <p class=\"tt\">Driver app and live dispatch map for delivery teams.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n    <a class=\"tile flip rv\" href=\"\/work\/ai-document-assistant\/\">\n      <div class=\"pic\"><span class=\"num\">04 \/ AI<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"AI document assistant answer screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">AI document assistant<\/h3> <p class=\"tt\">Ask your company files. Get answers with the source.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n    <a class=\"tile rv\" href=\"\/work\/booking-system\/\">\n      <div class=\"pic\"><span class=\"num\">05 \/ Systems<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"Booking system staff calendar screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">Booking system<\/h3> <p class=\"tt\">Online booking, staff calendar and reminders in one place.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n    <a class=\"tile flip rv\" href=\"\/work\/inventory-sales\/\">\n      <div class=\"pic\"><span class=\"num\">06 \/ Systems<\/span> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover-768x480.webp 768w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover-1024x640.webp 1024w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover-1536x960.webp 1536w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover.webp 1600w\" sizes=\"auto, 100vw\" alt=\"Inventory system stock overview screen\" loading=\"lazy\" width=\"1600\" height=\"1000\"><\/div>\n      <div class=\"meta\"><div><h3 class=\"label\">Inventory &amp; sales system<\/h3> <p class=\"tt\">Every branch's stock, sales and purchasing in one system.<\/p><\/div> <span class=\"go\"><svg><use href=\"#arr-up\"\/><\/svg><\/span><\/div>\n    <\/a>\n  <\/div>\n  <a class=\"more\" href=\"\/work\/\">All projects <svg><use href=\"#arr\"\/><\/svg><\/a>\n<\/section>\n\n\n<section class=\"band\">\n  <span class=\"label\">\/\/ Our promise<\/span>\n  <h2>Built around how you work.<\/h2>\n  <p>Tell us how your business runs. Then we design custom software around your business, build it and launch it with you.<\/p>\n  <div class=\"facts\">\n    <div><b>24\/7<\/b> always reachable<\/div>\n    <div><b>3<\/b> languages<\/div>\n    <div><b>Any<\/b> size, anywhere<\/div>\n  <\/div>\n  <p class=\"who\">Qcoders is a new software company, based in Turkey. <a href=\"\/about\/\">More about us<\/a><\/p>\n<\/section>\n\n\n<section>\n  <div class=\"sh sh-block\"><span class=\"label\">\/\/ Services<\/span><h2>Six ways we can help<\/h2><p class=\"intro\">Twelve services in six groups. Pick the one that sounds like your week.<\/p><\/div>\n  <ul class=\"svc\">\n    <li><a href=\"\/services\/#web\"><span class=\"n\">01<\/span> <span><strong>Websites<\/strong> <small>A website that says what you do and makes it easy to contact you.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n    <li><a href=\"\/services\/#stores\"><span class=\"n\">02<\/span> <span><strong>Online stores<\/strong> <small>Products, payments, shipping and orders in one place.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n    <li><a href=\"\/services\/#apps\"><span class=\"n\">03<\/span> <span><strong>Mobile apps<\/strong> <small>Android and iPhone apps, when your project really needs one.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n    <li><a href=\"\/services\/#systems\"><span class=\"n\">04<\/span> <span><strong>Business systems<\/strong> <small>Customers, stock and staff in one system, including automation and dashboards.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n    <li><a href=\"\/services\/#ai\"><span class=\"n\">05<\/span> <span><strong>AI solutions<\/strong> <small>Answers for your customers and your team, from your own files.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n    <li><a href=\"\/services\/#support\"><span class=\"n\">06<\/span> <span><strong>Support after launch<\/strong> <small>Hosting, updates, security and honest advice before you spend.<\/small><\/span><svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a><\/li>\n  <\/ul>\n<\/section>\n\n\n<section>\n  <div class=\"sh sh-block\"><span class=\"label\">\/\/ The process<\/span><h2>Your project in four steps<\/h2><\/div>\n  <ol class=\"rows\">\n    <li class=\"rv\"><span class=\"n\" aria-hidden=\"true\">01<\/span><div><h3>Listen<\/h3><p>It starts with a message, then a short call about your business and what takes your time. So you don't need to know what to ask for.<\/p><p class=\"you\">You get plain language: every technical word explained, or removed.<\/p><\/div><\/li>\n    <li class=\"rv\"><span class=\"n\" aria-hidden=\"true\">02<\/span><div><h3>Decide<\/h3><p>New system or ready-made tool? Then you see the options side by side, in plain words, with a clear recommendation.<\/p><p class=\"you\">You get a written proposal: scope, stages, cost and timeline.<\/p><\/div><\/li>\n    <li class=\"rv\"><span class=\"n\" aria-hidden=\"true\">03<\/span><div><h3>Build<\/h3><p>Your system is built in stages, and you see each one working, so progress is never a guess.<\/p><p class=\"you\">You get something working early, and no surprises at the end.<\/p><\/div><\/li>\n    <li class=\"rv\"><span class=\"n\" aria-hidden=\"true\">04<\/span><div><h3>Stay<\/h3><p>Your team learns the system during handover. After that it stays hosted, updated and fixed, if you want that.<\/p><p class=\"you\">You get a partner after launch, on a support plan that says what it covers.<\/p><\/div><\/li>\n  <\/ol>\n  <a class=\"more\" href=\"\/how-we-work\/\">See how we work <svg aria-hidden=\"true\"><use href=\"#arr\"\/><\/svg><\/a>\n<\/section>\n\n\n<section class=\"cta\">\n  <span class=\"label\">\/\/ Contact<\/span>\n  <h2>Tell us what takes your time<\/h2>\n  <p class=\"sub\">Pick what you need, then we'll write the first message for you. No tech words needed.<\/p>\n  <span class=\"label dim needs-q\">What do you need?<\/span>\n  <ul class=\"needs\" aria-label=\"What do you need?\"><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20a%20website.\" target=\"_blank\" rel=\"noopener\">Website<\/a><\/li><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20an%20online%20store.\" target=\"_blank\" rel=\"noopener\">Online store<\/a><\/li><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20a%20mobile%20app.\" target=\"_blank\" rel=\"noopener\">App<\/a><\/li><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20a%20business%20system.\" target=\"_blank\" rel=\"noopener\">Business system<\/a><\/li><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20an%20AI%20assistant.\" target=\"_blank\" rel=\"noopener\">AI<\/a><\/li><li><a href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20help%20deciding.\" target=\"_blank\" rel=\"noopener\">Not sure yet<\/a><\/li><\/ul>\n  <a class=\"wa\" href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.\" target=\"_blank\" rel=\"noopener\">\n    <span class=\"ic\"><svg><use href=\"#wa\"\/><\/svg><\/span>\n    <span><b>Chat on WhatsApp<\/b> <span>+966 50 760 0156<\/span><\/span>\n    <svg class=\"arr\"><use href=\"#arr\"\/><\/svg>\n  <\/a>\n  <a class=\"alt\" href=\"mailto:info@qcoders.com\"><span><span class=\"label dim\">\/\/ Email<\/span><br><b>info@qcoders.com<\/b><\/span><svg width=\"22\" height=\"22\" style=\"color:var(--volt)\"><use href=\"#arr\"\/><\/svg><\/a>\n  <div class=\"langs\"><span>English<\/span> <span lang=\"ar\" dir=\"rtl\">\u0627\u0644\u0639\u0631\u0628\u064a\u0629<\/span> <span lang=\"tr\">T\u00fcrk\u00e7e<\/span><\/div>\n<\/section>\n\n\n\n\n\n<\/div>\n<script>(function(){var r=document.querySelector('.qcm');if(!r)return;r.classList.add('js');var els=r.querySelectorAll('.rv');\nif(!('IntersectionObserver' in window)){els.forEach(function(e){e.classList.add('in')});return;}\nvar io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target);}})},{rootMargin:'0px 0px -8% 0px'});\nels.forEach(function(e){io.observe(e)});})();<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3094b3f3 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"3094b3f3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-44e8b119 elementor-widget elementor-widget-html\" data-id=\"44e8b119\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n\n<link rel=\"preconnect\" href=\"https:\/\/cdnjs.cloudflare.com\">\n<link rel=\"preconnect\" href=\"https:\/\/cdn.jsdelivr.net\" crossorigin>\n<style>\n.qc-bp {\n  \/* Sticky site header. The script replaces this with the header's measured bottom edge (admin bar included). *\/\n  --qc-header-h: 76px;\n  --qc-pin-top: var(--qc-header-h);\n  \/* Elementor content width (Site Settings > Layout), so the copy lines up with the header logo and the sections below. *\/\n  --qc-box: var(--container-max-width, 1140px);\n  --gutter: max(24px, calc((100% - var(--qc-box)) \/ 2));\n\n  --ink: #0A0E17; --paper: #F6F5F0; --volt: #C8F53B;\n  --ink-900: #0F1420; --ink-800: #161D2C; --ink-700: #212A3B; --ink-600: #2F3A4F;\n  --ink-500: #4A5568; --ink-400: #6B7588; --ink-300: #9AA3B2; --volt-700: #6E9400;\n  --blue: #5B8CFF; --amber: #FFB547;\n  --sans: \"Readex Pro\", \"Segoe UI\", Tahoma, sans-serif;\n  --mono: \"JetBrains Mono\", \"Readex Pro\", Consolas, monospace;\n  --ease: cubic-bezier(.65, 0, .2, 1);\n  --out: cubic-bezier(.2, .8, .2, 1);\n  position: relative;\n  width: 100%;\n  background: var(--ink);\n  color: var(--paper);\n  font: 400 17px\/1.55 var(--sans);\n  text-align: left;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n@media (max-width: 767px) { .qc-bp { --qc-header-h: 64px; } }\n@media (max-width: 760px) { .qc-bp { --gutter: 24px; } }\n.qc-bp *, .qc-bp *::before, .qc-bp *::after { box-sizing: border-box; }\n\/* (0,1,1) + later in the page than the theme\/kit CSS, so it beats \".elementor-kit-N p\" without !important *\/\n.qc-bp :is(h1, h2, h3, p, ol, ul, li, dl, dt, dd, figure) { margin: 0; padding: 0; }\n.qc-bp :is(ol, ul) { list-style: none; }\n.qc-bp :is(svg, canvas) { display: block; }\n.qc-bp :focus-visible { outline: 2px solid var(--volt); outline-offset: 4px; border-radius: 6px; }\n.qc-bp .qc-bp-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n\n\/* ---------- shared bits ---------- *\/\n.qc-bp .qc-bp-label {\n  font: 500 14px\/1.4 var(--mono); color: var(--volt); letter-spacing: .02em; margin: 0; text-transform: none;\n}\n.qc-bp .qc-bp-sheet {\n  position: absolute; inset: 0; z-index: 0; pointer-events: none;\n  background-image:\n    radial-gradient(circle, rgba(246,245,240,.08) 1.2px, transparent 1.7px),\n    linear-gradient(rgba(246,245,240,.028) 1px, transparent 1px),\n    linear-gradient(90deg, rgba(246,245,240,.028) 1px, transparent 1px);\n  background-size: 44px 44px, 176px 176px, 176px 176px;\n  background-position: -22px -22px, 0 0, 0 0;\n}\n.qc-bp a.qc-bp-link, .qc-bp a.qc-bp-link:visited {\n  display: inline-flex; align-items: center; gap: 10px;\n  font: 500 16px\/1.2 var(--sans); letter-spacing: 0; text-transform: none; color: var(--paper); text-decoration: none;\n  padding: 6px 0; border: 0; background: none; box-shadow: none;\n}\n.qc-bp a.qc-bp-link span { background: linear-gradient(var(--ink-600), var(--ink-600)) 0 100% \/ 100% 1px no-repeat; padding-bottom: 3px; transition: background-size .3s var(--ease); }\n.qc-bp a.qc-bp-link:hover, .qc-bp a.qc-bp-link:focus { color: var(--paper); }\n.qc-bp a.qc-bp-link:hover span { background-image: linear-gradient(var(--volt), var(--volt)); }\n.qc-bp .qc-bp-link svg { width: 12px; height: 12px; flex: none; }\n\n\/* title block (drawing sheet) *\/\n.qc-bp .qc-bp-tb {\n  position: absolute; z-index: 3; width: 292px;\n  border: 1px solid var(--ink-700); background: rgba(10,14,23,.86);\n  font: 500 11px\/1.4 var(--mono); color: var(--ink-300);\n}\n.qc-bp .qc-bp-tb-head { display: flex; justify-content: space-between; gap: 12px; padding: 8px 12px; border-bottom: 1px solid var(--ink-700); color: var(--paper); }\n.qc-bp .qc-bp-tb-grid { display: grid; grid-template-columns: 74px 1fr; margin: 0; }\n.qc-bp .qc-bp-tb-grid dt, .qc-bp .qc-bp-tb-grid dd { margin: 0; padding: 7px 12px; border-bottom: 1px solid var(--ink-700); min-height: 32px; display: flex; align-items: center; font: inherit; }\n.qc-bp .qc-bp-tb-grid dt { border-right: 1px solid var(--ink-700); color: var(--ink-300); }\n.qc-bp .qc-bp-tb-grid dd { color: var(--paper); }\n.qc-bp .qc-bp-tb-grid > :nth-last-child(-n+2) { border-bottom: 0; }\n.qc-bp .qc-bp-status { display: inline-flex; align-items: center; gap: 8px; }\n.qc-bp .qc-bp-status i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-500); transition: background-color .3s; }\n.qc-bp .qc-bp-status[data-status=\"drafting\"] i { background: var(--blue); }\n.qc-bp .qc-bp-status[data-status=\"building\"] i, .qc-bp .qc-bp-status[data-status=\"rendering\"] i { background: var(--amber); }\n.qc-bp .qc-bp-status[data-status=\"running\"] i { background: var(--volt); }\n.qc-bp .qc-bp-segs { display: inline-flex; gap: 4px; }\n.qc-bp .qc-bp-seg, .qc-bp .qc-bp-seg:hover, .qc-bp .qc-bp-seg:focus {\n  appearance: none; -webkit-appearance: none; margin: 0; cursor: pointer; min-height: 0; width: auto;\n  font: 500 11px\/1 var(--mono); letter-spacing: 0; text-transform: none; text-decoration: none; white-space: nowrap;\n  color: var(--ink-300); background: transparent;\n  border: 1px solid var(--ink-400); border-radius: 4px; padding: 5px 8px; box-shadow: none;   \/* ink-400: 4.2:1 boundary on Ink *\/\n  transition: color .2s, background-color .2s, border-color .2s;\n}\n.qc-bp .qc-bp-seg:hover { color: var(--paper); border-color: var(--ink-300); }\n.qc-bp .qc-bp-seg[aria-pressed=\"true\"], .qc-bp .qc-bp-seg[aria-pressed=\"true\"]:hover, .qc-bp .qc-bp-seg[aria-pressed=\"true\"]:focus {\n  color: var(--ink); background: var(--paper); border-color: var(--paper);\n}\n@media (forced-colors: active) {   \/* High Contrast: the pressed view still shows *\/\n  .qc-bp .qc-bp-seg[aria-pressed=\"true\"], .qc-bp .qc-bp-seg[aria-pressed=\"true\"]:hover, .qc-bp .qc-bp-seg[aria-pressed=\"true\"]:focus {\n    forced-color-adjust: none; background: Highlight; color: HighlightText; border-color: Highlight;\n  }\n}\n\n\/* =====================================================================\n   HERO: fills the viewport below the sticky site header\n   ===================================================================== *\/\n.qc-bp .qc-bp-hero {\n  position: relative; isolation: isolate; overflow: hidden;\n  min-height: max(600px, calc(100vh - var(--qc-header-h)));\n  min-height: max(600px, calc(100svh - var(--qc-header-h)));\n  display: flex; flex-direction: column;\n}\n.qc-bp .qc-bp-hero .qc-bp-sheet {\n  -webkit-mask-image: radial-gradient(120% 90% at 66% 48%, #000 45%, rgba(0,0,0,.35) 100%);\n          mask-image: radial-gradient(120% 90% at 66% 48%, #000 45%, rgba(0,0,0,.35) 100%);\n}\n\n\/* rulers + crop marks + crosshair (desktop) *\/\n.qc-bp .qc-bp-ruler { position: absolute; z-index: 2; pointer-events: none; background-repeat: no-repeat; }\n.qc-bp .qc-bp-ruler--x {\n  top: 0; left: 0; right: 0; height: 14px; border-bottom: 1px solid var(--ink-700);\n  background-image:\n    repeating-linear-gradient(90deg, rgba(246,245,240,.26) 0 1px, transparent 1px 44px),\n    repeating-linear-gradient(90deg, rgba(246,245,240,.12) 0 1px, transparent 1px 11px);\n  background-size: 100% 9px, 100% 4px; background-position: 0 100%, 0 100%;\n}\n.qc-bp .qc-bp-ruler--y {\n  top: 0; left: 0; bottom: 0; width: 14px; border-right: 1px solid var(--ink-700);\n  background-image:\n    repeating-linear-gradient(180deg, rgba(246,245,240,.26) 0 1px, transparent 1px 44px),\n    repeating-linear-gradient(180deg, rgba(246,245,240,.12) 0 1px, transparent 1px 11px);\n  background-size: 9px 100%, 4px 100%; background-position: 100% 0, 100% 0;\n}\n.qc-bp .qc-bp-rmark { position: absolute; left: 0; top: 0; background: var(--volt); opacity: 0; transition: opacity .2s; }\n.qc-bp .qc-bp-ruler--x .qc-bp-rmark { width: 1px; height: 14px; }\n.qc-bp .qc-bp-ruler--y .qc-bp-rmark { width: 14px; height: 1px; }\n.qc-bp .qc-bp-hero.is-pointer .qc-bp-rmark { opacity: 1; }\n.qc-bp .qc-bp-crop i { position: absolute; z-index: 1; width: 14px; height: 14px; border: 0 solid var(--ink-500); pointer-events: none; }\n.qc-bp .qc-bp-crop i:nth-child(1) { top: 30px; left: 30px; border-top-width: 1px; border-left-width: 1px; }\n.qc-bp .qc-bp-crop i:nth-child(2) { top: 30px; right: 16px; border-top-width: 1px; border-right-width: 1px; }\n.qc-bp .qc-bp-crop i:nth-child(3) { bottom: 16px; left: 30px; border-bottom-width: 1px; border-left-width: 1px; }\n.qc-bp .qc-bp-crop i:nth-child(4) { bottom: 16px; right: 16px; border-bottom-width: 1px; border-right-width: 1px; }\n.qc-bp .qc-bp-cross { position: absolute; inset: 0; z-index: 2; pointer-events: none; opacity: 0; transition: opacity .25s; }\n.qc-bp .qc-bp-hero.is-pointer .qc-bp-cross { opacity: 1; }\n.qc-bp .qc-bp-cross-h, .qc-bp .qc-bp-cross-v { position: absolute; left: 0; top: 0; background: rgba(246,245,240,.075); }\n.qc-bp .qc-bp-cross-h { right: 0; height: 1px; }\n.qc-bp .qc-bp-cross-v { bottom: 0; width: 1px; }\n.qc-bp .qc-bp-cross-r { position: absolute; left: 0; top: 0; padding: 4px 6px; border-radius: 3px; background: rgba(10,14,23,.86); font: 500 11px\/1 var(--mono); color: var(--ink-300); white-space: pre; }\n.qc-bp .qc-bp-cross-s { position: absolute; left: -6px; top: -6px; width: 13px; height: 13px; border: 1px solid var(--volt); opacity: 0; }\n.qc-bp .qc-bp-cross.is-snap .qc-bp-cross-s { opacity: 1; }\n.qc-bp .qc-bp-cross.is-snap .qc-bp-cross-r { color: var(--volt); }\n.qc-bp .qc-bp-cross.is-snap .qc-bp-cross-h, .qc-bp .qc-bp-cross.is-snap .qc-bp-cross-v { background: rgba(200,245,59,.22); }\n\n\/* copy *\/\n.qc-bp .qc-bp-main { position: relative; z-index: 3; order: 2; flex: 1; display: flex; align-items: center; padding: 40px var(--gutter) 112px; }\n.qc-bp .qc-bp-copy { width: min(680px, 46vw); }\n.qc-bp .qc-bp-type { display: inline-block; white-space: nowrap; vertical-align: top; }   \/* no fixed width: user text spacing never clips it *\/\n.qc-bp .qc-bp-h1 {\n  margin: 22px 0 0; padding: 0;\n  font: 700 clamp(44px, 6.1vw, 92px)\/1 var(--sans); letter-spacing: -.04em; word-spacing: normal;\n  color: var(--paper); text-transform: none;\n}\n.qc-bp .qc-bp-line { position: relative; display: block; width: max-content; max-width: 100%; white-space: normal; }   \/* wraps only if it must (user text spacing) *\/\n.qc-bp .qc-bp-nowrap { white-space: nowrap; }   \/* keeps the cursor on the last word *\/\n.qc-bp .qc-bp-line-in { display: inline-block; padding-bottom: .06em; }\n.qc-bp .qc-bp-cursor { display: inline-block; width: .42em; height: .78em; margin-left: .1em; vertical-align: -.04em; border-radius: 3px; background: var(--volt); }\n.qc-bp .qc-bp-lede { margin: 30px 0 0; max-width: 33em; font: 400 19px\/1.55 var(--sans); color: var(--ink-300); }\n.qc-bp .qc-bp-ctas { margin-top: 40px; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 30px; }\n.qc-bp a.qc-bp-btn, .qc-bp a.qc-bp-btn:visited {\n  position: relative; isolation: isolate;\n  display: inline-flex; align-items: center; gap: 14px;\n  height: 56px; padding: 0 22px 0 24px; border-radius: 10px; border: 0;\n  font: 600 16px\/1 var(--sans); letter-spacing: -.005em; text-transform: none; color: var(--ink);\n  text-decoration: none; background: transparent; box-shadow: none;\n}\n.qc-bp a.qc-bp-btn::after { content: \"\"; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: var(--volt); transition: background-color .25s; }\n.qc-bp a.qc-bp-btn:hover, .qc-bp a.qc-bp-btn:focus { color: var(--ink); }\n.qc-bp a.qc-bp-btn:hover::after { background: var(--paper); }\n.qc-bp .qc-bp-btn svg { width: 14px; height: 14px; transition: transform .3s var(--ease); }\n.qc-bp a.qc-bp-btn:hover svg { transform: translateX(3px); }\n\n\/* stage: the drawing + the 3D mark *\/\n.qc-bp .qc-bp-stage { position: absolute; z-index: 1; top: 0; right: 0; bottom: 0; width: 60%; pointer-events: none; }\n.qc-bp .qc-bp-drawwrap { position: absolute; inset: 0; opacity: calc(1 - var(--exit, 0) * 1.5); }\n.qc-bp .qc-bp-draw { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; --s: 4; }   \/* the script sets the real scale *\/\n.qc-bp .qc-bp-canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity .4s; }\n.qc-bp .qc-bp-hero.is-3d .qc-bp-canvas { opacity: 1; }\n\n.qc-bp .qc-bp-draw .ln { fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: calc(1.2px \/ var(--s)); }\n.qc-bp .qc-bp-draw .gr { fill: none; stroke: rgba(246,245,240,.075); stroke-width: calc(1px \/ var(--s)); }\n.qc-bp .qc-bp-draw .ol { stroke: rgba(246,245,240,.8); }\n.qc-bp .qc-bp-draw .cl { stroke: rgba(246,245,240,.36); stroke-dasharray: calc(6px \/ var(--s)) calc(4px \/ var(--s)); }\n.qc-bp .qc-bp-draw .dm { stroke: var(--volt); }\n.qc-bp .qc-bp-draw .dx { stroke: rgba(200,245,59,.5); stroke-dasharray: calc(3px \/ var(--s)) calc(3px \/ var(--s)); }\n.qc-bp .qc-bp-draw .ax { stroke: rgba(200,245,59,.28); stroke-dasharray: calc(2px \/ var(--s)) calc(5px \/ var(--s)); }\n.qc-bp .qc-bp-draw .tx { fill: var(--volt); font-family: var(--mono); font-weight: 500; font-size: calc(11px \/ var(--s)); letter-spacing: .02em; }\n.qc-bp .qc-bp-draw .nd { fill: var(--ink); stroke: var(--volt); stroke-width: calc(1.2px \/ var(--s)); }\n.qc-bp .qc-bp-draw .icl { fill: none; stroke: var(--ink); stroke-width: calc(1.1px \/ var(--s)); stroke-dasharray: calc(5px \/ var(--s)) calc(3.5px \/ var(--s)); opacity: .75; }\n.qc-bp .qc-bp-dg, .qc-bp .qc-bp-dims { transition: opacity .9s var(--ease); }\n\/* the solid has turned away from the source drawing: its outline and dimensions step back *\/\n.qc-bp .qc-bp-hero.is-3d.is-solid .qc-bp-dg, .qc-bp .qc-bp-hero.is-3d.is-solid .qc-bp-dims { opacity: .5; }\n.qc-bp .qc-bp-flat { visibility: visible; }\n.qc-bp.qc-js .qc-bp-flat { visibility: hidden; }\n.qc-bp.qc-js .qc-bp-hero.is-flat .qc-bp-flat { visibility: visible; }\n\/* the 6 chevron nodes sit on the Paper strokes whenever the flat mark shows: Volt 700, never thin Volt on Paper *\/\n.qc-bp:not(.qc-js) .qc-bp-nodes .nd:nth-child(-n+6), .qc-bp .qc-bp-hero.is-flat.is-solid .qc-bp-nodes .nd:nth-child(-n+6) { stroke: var(--volt-700); }\n.qc-bp .qc-bp-sweep { transform-origin: 64.5px 64.5px; transform: rotate(45deg) translateX(0); transition: transform 1.15s var(--ease); }\n.qc-bp:not(.qc-js) .qc-bp-sweep, .qc-bp .qc-bp-hero.is-flat.is-solid .qc-bp-sweep { transform: rotate(45deg) translateX(170px); }\n\n.qc-bp .qc-bp-hero .qc-bp-tb { right: var(--gutter); bottom: 32px; z-index: 5; }   \/* above .qc-bp-main: abs. children of a flex box paint as order 0 *\/\n.qc-bp .qc-bp-scroll { position: absolute; z-index: 3; left: var(--gutter); bottom: 32px; display: flex; align-items: flex-end; gap: 14px; font: 500 11px\/1 var(--mono); color: var(--ink-300); }\n.qc-bp .qc-bp-scroll i { position: relative; display: block; width: 1px; height: 44px; background: var(--ink-700); overflow: hidden; }\n.qc-bp .qc-bp-scroll i::after { content: \"\"; position: absolute; left: 0; top: 0; width: 1px; height: 16px; background: var(--volt); transform: translateY(28px); }\n.qc-bp .qc-bp-hero.is-static .qc-bp-scroll { display: none; }   \/* section 2 is static: nothing builds on scroll *\/\n\n\/* ---------- hero motion (only when motion is welcome) ----------\n   Rule: the static styles ARE the final frame. Every intro animation only\n   describes where it starts (fill: both), so \"animation: none\" = finished. *\/\n.qc-bp .qc-bp-draw .a-pop { transform-box: fill-box; transform-origin: center; }\n.qc-bp .qc-bp-line::before, .qc-bp .qc-bp-line::after, .qc-bp a.qc-bp-btn::before { opacity: 0; }\n@media (prefers-reduced-motion: no-preference) {\n  .qc-bp .qc-bp-type { animation: qcbp-type .75s steps(15) .15s both; }\n  .qc-bp .qc-bp-line::before {\n    content: \"\"; position: absolute; left: -.06em; right: -.08em; top: .08em; bottom: 0;\n    border: 1px dashed rgba(246,245,240,.3); border-radius: 2px; pointer-events: none;\n    animation: qcbp-box 1.7s var(--d) both;\n  }\n  .qc-bp .qc-bp-line::after {\n    content: \"\"; position: absolute; top: .1em; bottom: .02em; left: 0; width: 3px; margin-left: -1px;\n    background: var(--volt); pointer-events: none;\n    animation: qcbp-caret .9s var(--ease) calc(var(--d) + .22s) both;\n  }\n  .qc-bp .qc-bp-line-in { animation: qcbp-reveal .9s var(--ease) calc(var(--d) + .22s) both; }\n  \/* every loop is finite and ends on a still frame within 5 s (WCAG 2.2.2) *\/\n  .qc-bp .qc-bp-cursor { animation: qcbp-wait 1.62s, qcbp-blink 1.1s steps(1) 1.62s 3; }\n  .qc-bp .qc-bp-lede { animation: qcbp-in .8s var(--out) 1.05s both; }\n  .qc-bp .qc-bp-ctas .qc-bp-link { animation: qcbp-in .8s var(--out) 1.4s both; }\n  \/* the WhatsApp CTA is readable from the first frame: Paper label while the Volt fill wipes in, Ink once it is half-way *\/\n  .qc-bp a.qc-bp-btn { animation: qcbp-btn-text .4s; }\n  .qc-bp a.qc-bp-btn::before { content: \"\"; position: absolute; inset: 0; border-radius: inherit; border: 1px dashed rgba(246,245,240,.55); pointer-events: none; animation: qcbp-box2 .8s 0s both; }\n  .qc-bp a.qc-bp-btn::after { animation: qcbp-fill .5s var(--ease) .15s both; }\n  .qc-bp .qc-bp-hero .qc-bp-tb, .qc-bp .qc-bp-scroll { animation: qcbp-in .8s var(--out) .2s both; }\n  .qc-bp .qc-bp-hero .qc-bp-tb { animation-delay: .9s; }\n  .qc-bp .qc-bp-scroll { animation-delay: 1.8s; }\n  .qc-bp .qc-bp-scroll i::after { animation: qcbp-drop 2.4s var(--ease) 2.4s; }\n\n  .qc-bp .qc-bp-draw .a-fade { animation: qcbp-in .7s var(--out) var(--d, 0s) both; }\n  .qc-bp .qc-bp-draw .a-draw { animation: qcbp-draw var(--t, 1s) var(--ease) var(--d, 0s) both; }\n  .qc-bp .qc-bp-draw .a-pop { animation: qcbp-pop .5s var(--out) var(--d, 0s) both; }\n  .qc-bp:not(.qc-js) .qc-bp-sweep { animation: qcbp-sweep 1.15s var(--ease) 1.8s both; }\n\n  \/* settle: once the intro time is over, snap to the final frame even if frames were throttled *\/\n  .qc-bp.is-settled .qc-bp-hero *, .qc-bp.is-settled .qc-bp-hero *::before, .qc-bp.is-settled .qc-bp-hero *::after { animation: none; }\n  \/* same names and timing as above, so the browser keeps these running instead of restarting them *\/\n  .qc-bp.is-settled .qc-bp-cursor { animation: qcbp-blink 1.1s steps(1) 1.62s 3; }\n  .qc-bp.is-settled .qc-bp-scroll i::after { animation: qcbp-drop 2.4s var(--ease) 2.4s; }\n}\n.qc-bp.is-snap .qc-bp-hero *, .qc-bp.is-snap .qc-bp-hero *::before, .qc-bp.is-snap .qc-bp-hero *::after { transition: none !important; }\n\/* Elementor editor (and init failure): show the finished frame, nothing moves *\/\n.qc-bp.qc-bp--still *, .qc-bp.qc-bp--still *::before, .qc-bp.qc-bp--still *::after { animation: none !important; transition: none !important; }\n@keyframes qcbp-type   { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }   \/* steps(15): one mono character per step *\/\n@keyframes qcbp-btn-text { from, to { color: var(--paper); } }\n@keyframes qcbp-box    { 0% { opacity: 0; clip-path: inset(0 100% 0 0); } 18% { opacity: 1; clip-path: inset(0 0 0 0); } 72% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes qcbp-box2   { 0% { opacity: 0; } 25% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes qcbp-caret  { 0% { left: 0; opacity: 1; } 90% { opacity: 1; } 100% { left: 100%; opacity: 0; } }\n@keyframes qcbp-reveal { from { clip-path: inset(-.3em 100% -.3em 0); } to { clip-path: inset(-.3em 0 -.3em 0); } }\n@keyframes qcbp-fill   { from { clip-path: inset(0 100% 0 0 round 10px); } to { clip-path: inset(0 0 0 0 round 10px); } }\n@keyframes qcbp-wait   { from, to { visibility: hidden; } }\n@keyframes qcbp-blink  { 50% { opacity: 0; } }\n@keyframes qcbp-in     { from { opacity: 0; } }\n@keyframes qcbp-draw   { from { stroke-dasharray: 1 1; stroke-dashoffset: 1; } to { stroke-dasharray: 1 1; stroke-dashoffset: 0; } }\n@keyframes qcbp-pop    { from { transform: scale(0); } }\n@keyframes qcbp-drop   { 0% { transform: translateY(-16px); } 100% { transform: translateY(44px); } }\n@keyframes qcbp-sweep  { from { transform: rotate(45deg) translateX(0); } }\n\n\/* =====================================================================\n   SECTION 2 \u00b7 exploded view (pinned below the sticky header)\n   ===================================================================== *\/\n.qc-bp .qc-bp-build { position: relative; background: var(--ink); color: var(--paper); border-top: 1px solid var(--ink-700); scroll-margin-top: var(--qc-pin-top); overflow-x: clip; }   \/* keeps the drawing from adding sideways scroll now that the pin itself only clips its paint *\/   \/* \"See what we build\" lands at the pin start *\/\n.qc-bp .qc-bp-pin {\n  position: relative;\n  height: calc(100vh - var(--qc-pin-top)); height: calc(100svh - var(--qc-pin-top)); min-height: 560px;\n  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 24px; align-items: center;\n  padding: 0 var(--gutter); overflow: visible; clip-path: inset(0);   \/* clip the paint only: a layout clip on the pinned box makes Chrome log big layout shifts (CLS) when it pins and releases *\/\n}\n.qc-bp .qc-bp-copy2 { position: relative; z-index: 2; max-width: 500px; }\n.qc-bp .qc-bp-h2 { margin: 16px 0 0; font: 700 clamp(34px, 3.6vw, 52px)\/1.04 var(--sans); letter-spacing: -.035em; word-spacing: normal; color: var(--paper); text-transform: none; }\n.qc-bp .qc-bp-intro { margin: 18px 0 0; font: 400 17px\/1.55 var(--sans); color: var(--ink-300); max-width: 30em; }\n.qc-bp .qc-bp-parts { margin: 30px 0 0; padding: 0; border-bottom: 1px solid var(--ink-700); }\n.qc-bp .qc-bp-part { position: relative; display: grid; grid-template-columns: 46px 1fr; padding: 15px 0 16px; border-top: 1px solid var(--ink-700); margin: 0; }\n.qc-bp .qc-bp-part-n { font: 500 13px\/26px var(--mono); color: var(--ink-300); transition: color .3s; }\n.qc-bp .qc-bp-part-t { margin: 0; font: 600 20px\/26px var(--sans); letter-spacing: -.01em; text-transform: none; color: var(--ink-300); transition: color .3s; }\n.qc-bp .qc-bp-part-dw { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .45s var(--ease); }\n.qc-bp .qc-bp-part-dw > div { overflow: hidden; }\n.qc-bp .qc-bp-part-d { margin: 5px 0 0; font: 400 15px\/1.5 var(--sans); color: var(--ink-300); }\n.qc-bp .qc-bp-part-bar { position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--volt); transform-origin: left; transform: scaleX(var(--r, 0)); }\n.qc-bp .qc-bp-part.is-active .qc-bp-part-n, .qc-bp .qc-bp-part.is-done .qc-bp-part-n { color: var(--volt); }\n.qc-bp .qc-bp-part.is-active .qc-bp-part-t, .qc-bp .qc-bp-part.is-done .qc-bp-part-t { color: var(--paper); }\n.qc-bp .qc-bp-conn { margin: 18px 0 0; display: flex; align-items: center; gap: 10px; font: 400 15px\/1.5 var(--sans); color: var(--ink-300); transition: color .4s; }\n.qc-bp .qc-bp-conn i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-600); flex: none; transition: background-color .4s; }\n.qc-bp .qc-bp-build.is-running .qc-bp-conn { color: var(--paper); }\n.qc-bp .qc-bp-build.is-running .qc-bp-conn i { background: var(--volt); }\n.qc-bp .qc-bp-copy2 .qc-bp-link { margin-top: 22px; }\n\n.qc-bp .qc-bp-stage2 { position: relative; height: 100%; min-width: 0; }\n.qc-bp .qc-bp-scene {\n  position: absolute; inset: 0;\n  --w: 400px; --h: 262px; --T: 10px; --t: 12px; --g: 150px;\n}\n.qc-bp .qc-bp-iso {\n  position: absolute; left: 47%; top: 50%;\n  width: var(--w); height: var(--h); margin: calc(var(--h) \/ -2) 0 0 calc(var(--w) \/ -2);\n  transform-style: preserve-3d; transform: rotateX(60deg) rotateZ(-45deg);\n  --p: 0;\n}\n.qc-bp:not(.qc-js) .qc-bp-iso { --p: 1; }\n.qc-bp .qc-bp-floor {\n  position: absolute; left: -45%; top: -45%; width: 190%; height: 190%;\n  transform: translateZ(calc(-1 * (var(--t) + var(--g)) - 44px));\n  background-image: linear-gradient(rgba(246,245,240,.07) 1px, transparent 1px), linear-gradient(90deg, rgba(246,245,240,.07) 1px, transparent 1px);\n  background-size: 35px 35px; background-position: center;\n  -webkit-mask-image: radial-gradient(closest-side, #000 25%, transparent 100%);\n          mask-image: radial-gradient(closest-side, #000 25%, transparent 100%);\n}\n.qc-bp .qc-bp-plate {\n  position: absolute; inset: 0; transform-style: preserve-3d;\n  transform: translateZ(calc((1 - var(--i)) * (var(--t) + var(--p) * var(--g))));\n  --r: 1;\n}\n.qc-bp.qc-js .qc-bp-plate { --r: 0; }\n.qc-bp .qc-bp-face { position: absolute; inset: 0; }\n.qc-bp .qc-bp-side { position: absolute; pointer-events: none; box-shadow: inset 0 0 0 1px rgba(246,245,240,.32); }\n.qc-bp .qc-bp-side::before { content: \"\"; position: absolute; inset: 0; }\n.qc-bp .qc-bp-side--b { left: 0; top: 100%; width: 100%; height: var(--T); transform-origin: 50% 0; transform: rotateX(-90deg); }\n.qc-bp .qc-bp-side--l { top: 0; right: 100%; width: var(--T); height: 100%; transform-origin: 100% 50%; transform: rotateY(-90deg); }\n.qc-bp .qc-bp-side--b::before { background: var(--sb, #252F42); clip-path: inset(0 calc((1 - var(--r)) * 100%) 0 0); }\n.qc-bp .qc-bp-side--l::before { background: var(--sl, #1A2232); opacity: clamp(0, calc(var(--r) * 25), 1); }\n.qc-bp .qc-bp-plate[data-i=\"0\"] { --sb: #C7CCD5; --sl: #9AA3B2; --scan: var(--volt-700); }\n.qc-bp .qc-bp-scan {\n  position: absolute; top: -10px; bottom: -10px; left: 0; width: 2px; margin-left: -1px;\n  background: var(--scan, var(--volt)); opacity: 0; transform: translateX(calc(var(--r) * var(--w)));\n}\n.qc-bp .qc-bp-plate.is-scan .qc-bp-scan { opacity: 1; }\n.qc-bp .qc-bp-ticks { position: absolute; inset: -7px; pointer-events: none; opacity: calc(1 - var(--r));\n  background:\n    linear-gradient(var(--volt), var(--volt)) 0 0 \/ 9px 1px, linear-gradient(var(--volt), var(--volt)) 0 0 \/ 1px 9px,\n    linear-gradient(var(--volt), var(--volt)) 100% 0 \/ 9px 1px, linear-gradient(var(--volt), var(--volt)) 100% 0 \/ 1px 9px,\n    linear-gradient(var(--volt), var(--volt)) 0 100% \/ 9px 1px, linear-gradient(var(--volt), var(--volt)) 0 100% \/ 1px 9px,\n    linear-gradient(var(--volt), var(--volt)) 100% 100% \/ 9px 1px, linear-gradient(var(--volt), var(--volt)) 100% 100% \/ 1px 9px;\n  background-repeat: no-repeat;\n}\n.qc-bp .qc-bp-guide {\n  position: absolute; width: 0; border-left: 1px dashed rgba(246,245,240,.3);\n  height: calc(2 * (var(--t) + var(--p) * var(--g)));\n  transform-origin: 0 0; transform: translateZ(calc(-1 * (var(--t) + var(--p) * var(--g)))) rotateX(90deg);\n  transition: border-color .5s;\n}\n.qc-bp .qc-bp-build.is-running .qc-bp-guide { border-left: 1px solid rgba(200,245,59,.75); }\n.qc-bp .qc-bp-packet { position: absolute; left: -2px; top: 0; width: 3px; height: 14px; background: var(--volt); opacity: 0; }\n.qc-bp .qc-bp-build.is-running .qc-bp-packet { opacity: 1; animation: qcbp-packet 2.2s var(--ease) 2; animation-delay: var(--pd, 0s); }   \/* 2 runs, then still *\/\n.qc-bp .qc-bp-build.is-running .qc-bp-packet--down { animation-name: qcbp-packet-down; }\n.qc-bp .qc-bp-build.is-offscreen .qc-bp-packet { animation-play-state: paused; }\n@keyframes qcbp-packet      { 0% { transform: translateY(0); } 100% { transform: translateY(calc(2 * (var(--t) + var(--g)) - 14px)); } }\n@keyframes qcbp-packet-down { 0% { transform: translateY(calc(2 * (var(--t) + var(--g)) - 14px)); } 100% { transform: translateY(0); } }\n\n.qc-bp .qc-bp-build:not(.is-in) .qc-bp-ui--wire, .qc-bp .qc-bp-build:not(.is-in) .qc-bp-side, .qc-bp .qc-bp-build:not(.is-in) .qc-bp-ticks { opacity: 0; }\n.qc-bp:not(.qc-js) .qc-bp-build .qc-bp-side { opacity: 1; }\n.qc-bp .qc-bp-ui--wire, .qc-bp .qc-bp-side { transition: opacity .7s var(--out); transition-delay: calc(var(--i) * .12s); }\n\n.qc-bp .qc-bp-callouts { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }\n.qc-bp .qc-bp-co { opacity: 0; }\n.qc-bp .qc-bp-co .lead { fill: none; stroke: rgba(200,245,59,.75); stroke-width: 1; }\n.qc-bp .qc-bp-co .dot { fill: var(--volt); }\n.qc-bp .qc-bp-co .ball { fill: var(--ink); stroke: var(--volt); stroke-width: 1.2; transition: fill .3s; }\n.qc-bp .qc-bp-co .num { font: 500 11px var(--mono); fill: var(--volt); text-anchor: middle; dominant-baseline: central; transition: fill .3s; }\n.qc-bp .qc-bp-co .name { font: 500 12px var(--mono); fill: var(--ink-300); dominant-baseline: central; transition: fill .3s; }\n.qc-bp .qc-bp-co.is-active .ball, .qc-bp .qc-bp-co.is-done .ball { fill: var(--volt); }\n.qc-bp .qc-bp-co.is-active .num, .qc-bp .qc-bp-co.is-done .num { fill: var(--ink); }\n.qc-bp .qc-bp-co.is-active .name, .qc-bp .qc-bp-co.is-done .name { fill: var(--paper); }\n.qc-bp .qc-bp-co.is-done:first-child .dot { fill: var(--volt-700); }   \/* sits on the finished Paper website plate *\/\n.qc-bp .qc-bp-build .qc-bp-tb { right: 0; bottom: 32px; }\n\n\/* ---------- UI mock-ups on the plates (decorative) ---------- *\/\n.qc-bp .qc-bp-ui { position: absolute; inset: 0; overflow: hidden; font-size: calc(var(--w) \/ 42); font-family: var(--sans); line-height: 1.2; color: var(--paper); text-align: left; }\n.qc-bp .qc-bp-ui--real { background: var(--ink-800); box-shadow: inset 0 0 0 1px var(--ink-600); clip-path: inset(0 calc((1 - var(--r)) * 100%) 0 0); }\n.qc-bp .qc-bp-ui i, .qc-bp .qc-bp-ui b, .qc-bp .qc-bp-ui span { display: block; font-style: normal; font-weight: inherit; }\n.qc-bp .qc-bp-ui .qc-img { position: relative; background: var(--ink-700); border-radius: .5em; overflow: hidden; }\n\n.qc-bp .qc-ui-web { display: flex; flex-direction: column; }\n.qc-bp .qc-ui-web.qc-bp-ui--real { background: var(--paper); box-shadow: inset 0 0 0 1px #E6E8EC; color: var(--ink); }\n.qc-bp .qc-ui-web .w-bar { flex: none; height: 2.3em; display: flex; align-items: center; gap: .45em; padding: 0 1.1em; background: #E6E8EC; box-shadow: inset 0 -1px 0 #C7CCD5; }\n.qc-bp .qc-ui-web .w-bar i { width: .6em; height: .6em; border-radius: 50%; background: #C7CCD5; }\n.qc-bp .qc-ui-web .w-url { margin-left: 1.2em; width: 15em; height: 1.35em; border-radius: .7em; background: #fff; font: 500 .72em\/1.88 var(--mono); color: var(--ink-500); padding: 0 1em; white-space: nowrap; }\n.qc-bp .qc-ui-web .w-nav { display: flex; align-items: center; gap: 1.2em; padding: 1.05em 1.7em .4em; }\n.qc-bp .qc-ui-web .w-logo { width: 4.6em; height: 1em; border-radius: .25em; background: var(--ink); }\n.qc-bp .qc-ui-web .w-links { display: flex; gap: 1.3em; margin-left: auto; font-size: .78em; color: var(--ink-500); }\n.qc-bp .qc-ui-web .w-cta { font-size: .78em; font-weight: 600; padding: .4em .9em; border-radius: .45em; background: var(--ink); color: var(--paper); }\n.qc-bp .qc-ui-web .w-hero { display: grid; grid-template-columns: 1.08fr 1fr; gap: 1.5em; padding: 1.1em 1.7em .9em; }\n.qc-bp .qc-ui-web .w-copy { display: flex; flex-direction: column; gap: .6em; justify-content: center; }\n.qc-bp .qc-ui-web .w-h { font-size: 2em; font-weight: 700; line-height: 1.02; letter-spacing: -.035em; color: var(--ink); }\n.qc-bp .qc-ui-web .w-p { height: .48em; border-radius: .24em; background: #C7CCD5; width: 94%; }\n.qc-bp .qc-ui-web .w-p--s { width: 66%; }\n.qc-bp .qc-ui-web .w-btn { align-self: flex-start; margin-top: .45em; font-size: .82em; font-weight: 600; padding: .55em 1.15em; border-radius: .5em; background: var(--ink); color: var(--paper); }\n.qc-bp .qc-ui-web .w-img { min-height: 9.6em; background: #E6E8EC; }\n.qc-bp .qc-ui-web .w-img::before { content: \"\"; position: absolute; width: 7em; height: 7em; border-radius: 50%; right: 1.2em; bottom: -1.6em; background: #C7CCD5; }\n.qc-bp .qc-ui-web .w-img::after { content: \"\"; position: absolute; width: 3.2em; height: 3.2em; border-radius: .6em; left: 1.4em; top: 1.4em; background: var(--ink); }\n.qc-bp .qc-ui-web .w-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1em; padding: .5em 1.7em 1.5em; }\n.qc-bp .qc-ui-web .w-card { display: flex; flex-direction: column; gap: .45em; }\n.qc-bp .qc-ui-web .w-card .qc-img { height: 3.6em; border-radius: .45em; background: #E6E8EC; }\n.qc-bp .qc-ui-web .w-card span { height: .45em; border-radius: .22em; background: #C7CCD5; width: 82%; }\n.qc-bp .qc-ui-web .w-card span + span { width: 42%; background: var(--ink); }\n\n.qc-bp .qc-ui-app { display: flex; align-items: center; justify-content: center; gap: 2.2em; }\n.qc-bp .qc-ui-app .a-ph { position: relative; width: 10.8em; height: 23.4em; border-radius: 1.6em; background: var(--ink-900); box-shadow: inset 0 0 0 .32em var(--ink-600); padding: 1.6em .95em .9em; display: flex; flex-direction: column; gap: .62em; }\n.qc-bp .qc-ui-app .a-ph::before { content: \"\"; position: absolute; top: .62em; left: 50%; width: 3em; height: .42em; margin-left: -1.5em; border-radius: .3em; background: var(--ink-600); }\n.qc-bp .qc-ui-app .a-top { display: flex; align-items: center; justify-content: space-between; }\n.qc-bp .qc-ui-app .a-title { font-size: 1.05em; font-weight: 600; }\n.qc-bp .qc-ui-app .a-av { width: 1.3em; height: 1.3em; border-radius: 50%; background: var(--ink-600); }\n.qc-bp .qc-ui-app .a-pills { display: flex; gap: .35em; }\n.qc-bp .qc-ui-app .a-pills i { height: 1.1em; flex: 1; border-radius: .55em; background: var(--ink-700); }\n.qc-bp .qc-ui-app .a-pills i:first-child { background: var(--paper); }\n.qc-bp .qc-ui-app .a-row { display: grid; grid-template-columns: 2em 1fr auto; gap: .5em; align-items: center; padding: .45em; border-radius: .55em; background: var(--ink-800); }\n.qc-bp .qc-ui-app .a-row .qc-img { width: 2em; height: 2em; border-radius: .45em; }\n.qc-bp .qc-ui-app .a-row span { height: .42em; border-radius: .21em; background: var(--ink-600); }\n.qc-bp .qc-ui-app .a-row b { width: .5em; height: .5em; border-radius: 50%; background: var(--ink-500); }\n.qc-bp .qc-ui-app .a-row b.on { background: var(--volt); }\n.qc-bp .qc-ui-app .a-tab { margin-top: auto; display: flex; justify-content: space-around; padding-top: .6em; box-shadow: inset 0 1px 0 var(--ink-700); }\n.qc-bp .qc-ui-app .a-tab i { width: 1em; height: 1em; border-radius: .3em; background: var(--ink-600); }\n.qc-bp .qc-ui-app .a-tab i.on { background: var(--volt); }\n.qc-bp .qc-ui-app .a-hero { height: 8.6em; border-radius: .9em; }\n.qc-bp .qc-ui-app .a-hero::after { content: \"\"; position: absolute; left: 50%; top: 50%; width: 4.4em; height: 4.4em; margin: -2.2em 0 0 -2.2em; border-radius: 50%; background: var(--ink-600); }\n.qc-bp .qc-ui-app .a-h { font-size: 1.05em; font-weight: 600; }\n.qc-bp .qc-ui-app .a-line { height: .42em; border-radius: .21em; background: var(--ink-600); width: 86%; }\n.qc-bp .qc-ui-app .a-line--s { width: 52%; }\n.qc-bp .qc-ui-app .a-btn { margin-top: auto; height: 2.2em; border-radius: .6em; background: var(--volt); color: var(--ink); font-size: .8em; font-weight: 600; text-align: center; line-height: 2.75; }\n.qc-bp .qc-ui-app .a-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: .28em; }\n.qc-bp .qc-ui-app .a-cal i { aspect-ratio: 1; border-radius: .25em; background: var(--ink-700); }\n.qc-bp .qc-ui-app .a-cal i.on { background: var(--volt); }\n.qc-bp .qc-ui-app .a-slots { display: grid; grid-template-columns: 1fr 1fr; gap: .35em; }\n.qc-bp .qc-ui-app .a-slots i { height: 1.4em; border-radius: .4em; background: var(--ink-700); }\n.qc-bp .qc-ui-app .a-slots i.on { background: var(--paper); }\n\n.qc-bp .qc-ui-sys { display: grid; grid-template-columns: 7.4em 1fr; }\n.qc-bp .qc-ui-sys .s-side { background: var(--ink-900); box-shadow: inset -1px 0 0 var(--ink-700); padding: 1.2em .95em; display: flex; flex-direction: column; gap: .8em; }\n.qc-bp .qc-ui-sys .s-logo { width: 2em; height: 2em; border-radius: .5em; background: var(--paper); margin-bottom: .5em; }\n.qc-bp .qc-ui-sys .s-side span { height: .55em; border-radius: .28em; background: var(--ink-600); width: 90%; }\n.qc-bp .qc-ui-sys .s-side span.on { background: var(--volt); width: 74%; }\n.qc-bp .qc-ui-sys .s-main { padding: 1.15em 1.35em; display: flex; flex-direction: column; gap: .9em; min-width: 0; }\n.qc-bp .qc-ui-sys .s-top { display: flex; align-items: center; justify-content: space-between; }\n.qc-bp .qc-ui-sys .s-title { font-size: 1.3em; font-weight: 600; letter-spacing: -.01em; }\n.qc-bp .qc-ui-sys .s-search { width: 9em; height: 1.5em; border-radius: .75em; background: var(--ink-700); }\n.qc-bp .qc-ui-sys .s-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: .8em; }\n.qc-bp .qc-ui-sys .s-kpi { background: var(--ink-700); border-radius: .6em; padding: .75em .85em; display: flex; flex-direction: column; gap: .6em; }\n.qc-bp .qc-ui-sys .s-kpi span { font: 500 .72em\/1 var(--mono); color: var(--ink-300); }\n.qc-bp .qc-ui-sys .s-kpi b { height: 1em; border-radius: .3em; background: var(--paper); width: var(--v); }\n.qc-bp .qc-ui-sys .s-row2 { display: grid; grid-template-columns: 1.65fr 1fr; gap: .8em; flex: 1; min-height: 0; }\n.qc-bp .qc-ui-sys .s-chart { background: var(--ink-700); border-radius: .6em; padding: .9em; display: flex; align-items: flex-end; gap: .42em; }\n.qc-bp .qc-ui-sys .s-chart i { flex: 1; height: calc(var(--h) * 100%); border-radius: .2em .2em 0 0; background: var(--ink-500); }\n.qc-bp .qc-ui-sys .s-chart i:last-child { background: var(--volt); }\n.qc-bp .qc-ui-sys .s-list { background: var(--ink-700); border-radius: .6em; padding: .9em; display: flex; flex-direction: column; justify-content: space-between; }\n.qc-bp .qc-ui-sys .s-list span { height: .5em; border-radius: .25em; background: var(--ink-500); }\n.qc-bp .qc-ui-sys .s-list span:nth-child(odd) { width: 70%; }\n\n\/* wireframe version of the same UI (cloned by the script) *\/\n.qc-bp .qc-bp-ui--wire { background: transparent !important; box-shadow: inset 0 0 0 1px rgba(246,245,240,.5) !important; }\n.qc-bp .qc-bp-ui--wire * { background: transparent !important; color: transparent !important; opacity: 1 !important; box-shadow: inset 0 0 0 1px rgba(246,245,240,.2) !important; }\n.qc-bp .qc-bp-ui--wire *::before, .qc-bp .qc-bp-ui--wire *::after { background: transparent !important; box-shadow: inset 0 0 0 1px rgba(246,245,240,.2) !important; }\n.qc-bp .qc-bp-ui--wire .qc-img {\n  background:\n    linear-gradient(to top right, transparent calc(50% - .7px), rgba(246,245,240,.24) 50%, transparent calc(50% + .7px)),\n    linear-gradient(to bottom right, transparent calc(50% - .7px), rgba(246,245,240,.24) 50%, transparent calc(50% + .7px)) !important;\n}\n.qc-bp .qc-bp-ui--wire .qc-img::before, .qc-bp .qc-bp-ui--wire .qc-img::after { box-shadow: none !important; }\n.qc-bp .qc-bp-ui--wire :is(.a-row, .s-kpi, .s-chart, .s-list, .w-copy, .w-links, .a-pills, .a-cal, .a-slots, .s-kpis, .s-row2, .w-hero, .w-cards, .w-card, .a-top, .s-top, .s-main) { box-shadow: none !important; }\n\n\/* static section 2 (reduced motion, no GSAP, Elementor editor): normal flow, nothing clipped *\/\n.qc-bp .qc-bp-build.is-static .qc-bp-pin { height: auto; min-height: max(600px, calc(100svh - var(--qc-pin-top))); padding-block: 56px; }\n.qc-bp .qc-bp-build.is-static .qc-bp-stage2 { min-height: 800px; }\n\n\/* =====================================================================\n   RESPONSIVE\n   ===================================================================== *\/\n@media (max-width: 1100px) {\n  .qc-bp .qc-bp-scene { --w: 340px; --h: 222px; --g: 128px; }\n  .qc-bp .qc-bp-iso { left: 44%; }\n  .qc-bp .qc-bp-copy { width: min(560px, 50vw); }\n}\n\/* short desktop\/laptop screens (1366 x 768 and similar): keep everything inside the pinned frame *\/\n@media (min-width: 761px) and (max-height: 820px) {\n  .qc-bp .qc-bp-main { padding-top: 24px; padding-bottom: 96px; }\n  .qc-bp .qc-bp-h1 { font-size: clamp(44px, min(6.1vw, 10.5vh), 92px); }\n  .qc-bp .qc-bp-lede { margin-top: 22px; }\n  .qc-bp .qc-bp-ctas { margin-top: 30px; }\n  .qc-bp .qc-bp-h2 { font-size: clamp(30px, min(3.6vw, 6vh), 52px); }\n  .qc-bp .qc-bp-build:not(.is-static) .qc-bp-intro { display: none; }\n  .qc-bp .qc-bp-parts { margin-top: 22px; }\n  .qc-bp .qc-bp-part { padding: 11px 0 12px; }\n  .qc-bp .qc-bp-build:not(.is-static) .qc-bp-part:not(.is-active) .qc-bp-part-dw { grid-template-rows: 0fr; }\n  .qc-bp .qc-bp-scene { --g: 118px; }\n  .qc-bp .qc-bp-iso { top: 45%; }\n}\n\/* stacked layout (drawing above the copy): phones held upright, tablets held upright, and very narrow windows\n   (400% zoom). Phones held sideways keep the side-by-side layout. Keep in sync with STACKED_Q in the script. *\/\n@media (max-width: 760px) and (min-height: 501px), (max-width: 1100px) and (orientation: portrait), (max-width: 520px) {\n  .qc-bp .qc-bp-ruler, .qc-bp .qc-bp-cross, .qc-bp .qc-bp-scroll { display: none; }\n  .qc-bp .qc-bp-crop i:nth-child(1), .qc-bp .qc-bp-crop i:nth-child(2) { top: 12px; }\n  .qc-bp .qc-bp-crop i:nth-child(1), .qc-bp .qc-bp-crop i:nth-child(3) { left: 8px; }\n  .qc-bp .qc-bp-crop i:nth-child(2), .qc-bp .qc-bp-crop i:nth-child(4) { right: 8px; }\n  .qc-bp .qc-bp-crop i:nth-child(3), .qc-bp .qc-bp-crop i:nth-child(4) { bottom: 8px; }\n  .qc-bp .qc-bp-hero { min-height: max(520px, calc(100vh - var(--qc-header-h))); min-height: max(520px, calc(100svh - var(--qc-header-h))); }\n  .qc-bp .qc-bp-stage { position: relative; order: 1; top: auto; right: auto; bottom: auto; width: auto; height: 37svh; min-height: 250px; flex: none; }\n  .qc-bp .qc-bp-main { padding: 0 var(--gutter) 32px; align-items: flex-start; }\n  .qc-bp .qc-bp-copy { width: 100%; }\n  .qc-bp .qc-bp-draw { --s: 1.6; }\n  \/* the view switch follows the CTAs, in the visual order and the focus order alike *\/\n  .qc-bp .qc-bp-hero .qc-bp-tb { position: relative; order: 3; top: auto; right: auto; bottom: auto; align-self: flex-start; width: auto; margin: -8px var(--gutter) 28px; border: 0; background: none; font-size: 12px; }\n  .qc-bp .qc-bp-hero .qc-bp-tb-head, .qc-bp .qc-bp-hero .qc-bp-tb-grid dt, .qc-bp .qc-bp-hero .qc-bp-tb-grid dd:first-of-type { display: none; }\n  .qc-bp .qc-bp-hero .qc-bp-tb-grid { display: flex; align-items: center; gap: 12px; }\n  .qc-bp .qc-bp-hero .qc-bp-tb-grid dd { padding: 0; min-height: 0; border: 0; }\n  .qc-bp .qc-bp-hero .qc-bp-seg { font-size: 13px; padding: 9px 12px; }   \/* about 33 px tall touch targets *\/\n\n  .qc-bp .qc-bp-pin { grid-template-columns: 1fr; grid-template-rows: auto 1fr; align-items: start; gap: 0; padding: 0 var(--gutter); min-height: 540px; }\n  .qc-bp .qc-bp-stage2 { order: -1; height: 47svh; min-height: 300px; margin: 0 calc(-1 * var(--gutter)); }\n  .qc-bp .qc-bp-iso { left: 50%; top: 52%; }\n  .qc-bp .qc-bp-copy2 { max-width: none; }\n  .qc-bp .qc-bp-build:not(.is-static) .qc-bp-intro { display: none; }\n  .qc-bp .qc-bp-build:not(.is-static) .qc-bp-part:not(.is-active) .qc-bp-part-dw { grid-template-rows: 0fr; }\n  .qc-bp .qc-bp-build .qc-bp-tb { display: none; }\n  .qc-bp .qc-bp-build.is-static .qc-bp-pin { height: auto; min-height: 0; padding: 0 var(--gutter) 56px; }\n  .qc-bp .qc-bp-build.is-static .qc-bp-stage2 { min-height: 300px; }\n}\n\/* phones *\/\n@media (max-width: 760px) {\n  .qc-bp .qc-bp-label { font-size: 13px; }\n  .qc-bp .qc-bp-h1 { margin-top: 14px; font-size: min(11.2vw, 52px); }\n  .qc-bp .qc-bp-lede { margin-top: 18px; font-size: 17px; }\n  .qc-bp .qc-bp-ctas { margin-top: 24px; gap: 12px 24px; }\n  .qc-bp a.qc-bp-btn { width: 100%; justify-content: space-between; height: 54px; }\n  .qc-bp .qc-bp-scene { --w: 220px; --h: 144px; --T: 7px; --t: 9px; --g: 74px; }\n  .qc-bp .qc-bp-h2 { margin-top: 8px; font-size: 30px; }\n  .qc-bp .qc-bp-parts { margin-top: 16px; }\n  .qc-bp .qc-bp-part { padding: 10px 0 11px; grid-template-columns: 36px 1fr; }\n  .qc-bp .qc-bp-part-t { font-size: 17px; line-height: 24px; }\n  .qc-bp .qc-bp-part-n { line-height: 24px; font-size: 12px; }\n  .qc-bp .qc-bp-part-d { font-size: 14px; }\n  .qc-bp .qc-bp-conn { margin-top: 12px; font-size: 14px; }\n  .qc-bp .qc-bp-copy2 .qc-bp-link { margin-top: 8px; font-size: 15px; }\n  .qc-bp .qc-bp-co .name { display: none; }\n}\n\/* stacked phones, section 2: the drawing takes the height the copy leaves (the copy's tallest state, one part\n   description open, is about 390 px), so the pinned frame fits on most phones. Where it still can't, the script\n   shows the static layout instead. *\/\n@media (max-width: 760px) and (min-height: 501px), (max-width: 520px) {\n  .qc-bp .qc-bp-stage2 {\n    height: clamp(190px, calc(100vh - var(--qc-pin-top) - 400px), 47vh);\n    height: clamp(190px, calc(100svh - var(--qc-pin-top) - 400px), 47svh); min-height: 0;\n  }\n}\n\/* the exploded stack is about 0.35 * (w + h) + 1.73 * (t + g) + T tall: keep it inside the shorter stages *\/\n@media (max-width: 760px) and (max-height: 860px) { .qc-bp .qc-bp-scene { --g: 64px; } }\n@media (max-width: 760px) and (max-height: 760px) { .qc-bp .qc-bp-scene { --g: 48px; } }\n\/* tablets held upright: the stacked layout at tablet sizes *\/\n@media (min-width: 761px) and (max-width: 1100px) and (orientation: portrait) {\n  .qc-bp .qc-bp-stage { height: 44svh; }\n  .qc-bp .qc-bp-main { align-items: center; padding-bottom: 56px; }\n  .qc-bp .qc-bp-copy { max-width: 680px; }\n  .qc-bp .qc-bp-h1 { font-size: clamp(56px, 8.4vw, 84px); }\n  .qc-bp .qc-bp-scene { --w: 380px; --h: 249px; --g: 112px; }\n  .qc-bp .qc-bp-stage2 { height: 50svh; }\n  .qc-bp .qc-bp-copy2 { max-width: 640px; }\n}\n\/* short phones held upright (iPhone SE\/8 with Safari's bars, Androids with about 640 px visible):\n   the WhatsApp CTA stays above the fold and the pinned frame fits under the header *\/\n@media (max-width: 760px) and (min-height: 501px) and (max-height: 700px), (max-width: 760px) and (max-height: 700px) and (orientation: portrait) {\n  .qc-bp .qc-bp-stage { height: 28svh; min-height: 170px; }\n  .qc-bp .qc-bp-h1 { margin-top: 10px; font-size: min(10.4vw, 46px); }\n  .qc-bp .qc-bp-lede { margin-top: 12px; font-size: 16px; }\n  .qc-bp .qc-bp-ctas { margin-top: 18px; }\n  .qc-bp .qc-bp-pin { min-height: 0; }\n  .qc-bp .qc-bp-stage2 {\n    height: clamp(190px, calc(100vh - var(--qc-pin-top) - 380px), 34vh);\n    height: clamp(190px, calc(100svh - var(--qc-pin-top) - 380px), 34svh); min-height: 0;\n  }\n  .qc-bp .qc-bp-scene { --w: 184px; --h: 120px; --T: 6px; --t: 8px; --g: 38px; }   \/* about 192 px tall exploded *\/\n  .qc-bp .qc-bp-iso { top: 50%; }\n  .qc-bp .qc-bp-h2 { font-size: 26px; }\n  .qc-bp .qc-bp-part { padding: 8px 0 9px; }\n}\n\/* phones held sideways (about 360-430 px tall): no 600 px minimums; section 2 is static (the script skips the pin) *\/\n@media (orientation: landscape) and (max-height: 500px) {\n  .qc-bp .qc-bp-hero { min-height: 0; }\n  .qc-bp .qc-bp-main { padding: 20px var(--gutter) 28px; }\n  .qc-bp .qc-bp-h1 { margin-top: 10px; font-size: clamp(30px, 10svh, 44px); }\n  .qc-bp .qc-bp-lede { margin-top: 12px; font-size: 15px; }\n  .qc-bp .qc-bp-ctas { margin-top: 16px; }\n  .qc-bp .qc-bp-scroll, .qc-bp .qc-bp-hero .qc-bp-tb, .qc-bp .qc-bp-hero .qc-bp-crop { display: none; }\n  .qc-bp .qc-bp-build.is-static .qc-bp-stage2 { min-height: 520px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .qc-bp *, .qc-bp *::before, .qc-bp *::after { animation: none !important; transition-duration: .01s !important; transition-delay: 0s !important; }\n}\n<\/style>\n\n<div class=\"qc-bp\" data-qcbp>\n<script data-cfasync=\"false\">\/* Qcoders Blueprint: JS is on (the Elementor editor gets the finished, still frame instead) *\/\n(function (d) {\n  var s = d.currentScript, r = s && s.parentNode;\n  if (!r || !r.hasAttribute || !r.hasAttribute('data-qcbp')) { var a = d.querySelectorAll('[data-qcbp]'); r = a[a.length - 1]; }\n  if (!r) return;\n  var editor = \/[?&]elementor-preview=\/.test(location.search) || (d.body && d.body.classList.contains('elementor-editor-active'));\n  r.classList.add(editor ? 'qc-bp--still' : 'qc-js');\n  \/* safety net: if the module never starts (blocked CDN, old browser), fall back to the no-JS frame *\/\n  window.addEventListener('load', function () { setTimeout(function () { if (!r.hasAttribute('data-qcbp-ready')) r.classList.remove('qc-js'); }, 1500); });\n})(document);\n<\/script>\n\n\n<section class=\"qc-bp-hero\" aria-labelledby=\"qc-bp-h1\">\n  <div class=\"qc-bp-sheet\" aria-hidden=\"true\"><\/div>\n  <div class=\"qc-bp-crop\" aria-hidden=\"true\"><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\n  <div class=\"qc-bp-ruler qc-bp-ruler--x\" aria-hidden=\"true\"><i class=\"qc-bp-rmark\"><\/i><\/div>\n  <div class=\"qc-bp-ruler qc-bp-ruler--y\" aria-hidden=\"true\"><i class=\"qc-bp-rmark\"><\/i><\/div>\n\n  <div class=\"qc-bp-stage\" aria-hidden=\"true\">\n    <div class=\"qc-bp-drawwrap\">\n      <svg class=\"qc-bp-draw\" viewBox=\"-71 -29 214 205\" preserveAspectRatio=\"xMidYMid meet\" focusable=\"false\">\n        <defs>\n          <pattern id=\"qcbp-g8\" width=\"8\" height=\"8\" patternUnits=\"userSpaceOnUse\"><path class=\"gr\" d=\"M8 0H0V8\"\/><\/pattern>\n          <radialGradient id=\"qcbp-rg\" cx=\"64.5\" cy=\"64.5\" r=\"82\" gradientUnits=\"userSpaceOnUse\">\n            <stop offset=\"0\" stop-color=\"#fff\"\/><stop offset=\".55\" stop-color=\"#fff\" stop-opacity=\".7\"\/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"\/>\n          <\/radialGradient>\n          <mask id=\"qcbp-fade\" maskUnits=\"userSpaceOnUse\" x=\"-30\" y=\"-30\" width=\"190\" height=\"190\"><rect x=\"-30\" y=\"-30\" width=\"190\" height=\"190\" fill=\"url(#qcbp-rg)\"\/><\/mask>\n          <clipPath id=\"qcbp-clip\"><rect class=\"qc-bp-sweep\" x=\"-315.5\" y=\"-135.5\" width=\"300\" height=\"400\"\/><\/clipPath>\n          <path id=\"qcbp-pl\" d=\"M51 28 L19 60 L51 92\"\/>\n          <path id=\"qcbp-pr\" d=\"M69 28 L101 60 L69 92\"\/>\n          <path id=\"qcbp-pt\" d=\"M97 88 L110 101\"\/>\n        <\/defs>\n\n        <rect class=\"a-fade\" style=\"--d:.05s\" x=\"-17\" y=\"-8\" width=\"163\" height=\"145\" fill=\"url(#qcbp-g8)\" mask=\"url(#qcbp-fade)\"\/>\n\n        <g class=\"qc-bp-dg\">\n          <use class=\"ln cl a-fade\" href=\"#qcbp-pl\" style=\"--d:.25s\"\/>\n          <use class=\"ln cl a-fade\" href=\"#qcbp-pr\" style=\"--d:.35s\"\/>\n          <use class=\"ln cl a-fade\" href=\"#qcbp-pt\" style=\"--d:.45s\"\/>\n          <path class=\"ln ol a-draw\" pathLength=\"1\" style=\"--d:.5s;--t:1.15s\" d=\"M46.757 23.757 L14.757 55.757 A6 6 0 0 0 14.757 64.243 L46.757 96.243 A6 6 0 0 0 55.243 87.757 L27.485 60 L55.243 32.243 A6 6 0 0 0 46.757 23.757 Z\"\/>\n          <path class=\"ln ol a-draw\" pathLength=\"1\" style=\"--d:.68s;--t:1.15s\" d=\"M73.243 23.757 L105.243 55.757 A6 6 0 0 1 105.243 64.243 L73.243 96.243 A6 6 0 0 1 64.757 87.757 L92.515 60 L64.757 32.243 A6 6 0 0 1 73.243 23.757 Z\"\/>\n          <path class=\"ln ol a-draw\" pathLength=\"1\" style=\"--d:.95s;--t:.8s\" d=\"M101.243 83.757 L114.243 96.757 A6 6 0 0 1 105.757 105.243 L92.757 92.243 A6 6 0 0 1 101.243 83.757 Z\"\/>\n        <\/g>\n\n        <g class=\"qc-bp-dims\">\n          <path class=\"ln ax a-fade\" style=\"--d:1.35s\" d=\"M53 44 L124 115\"\/>\n          \n          <path class=\"ln dx a-fade\" style=\"--d:1.1s\" d=\"M51 20 V10.5 M69 20 V10.5\"\/>\n          <path class=\"ln dm a-draw\" pathLength=\"1\" style=\"--d:1.15s;--t:.5s\" d=\"M51 14.5 H69 M49.7 15.8 L52.3 13.2 M67.7 15.8 L70.3 13.2\"\/>\n          <text class=\"tx a-fade\" style=\"--d:1.4s\" x=\"60\" y=\"9.6\" text-anchor=\"middle\">gap 18u<\/text>\n          \n          <path class=\"ln dx a-fade\" style=\"--d:1.15s\" d=\"M47.5 22 H1 M34.5 34 H1\"\/>\n          <path class=\"ln dm a-draw\" pathLength=\"1\" style=\"--d:1.2s;--t:.5s\" d=\"M4 22 V34 M2.7 23.3 L5.3 20.7 M2.7 35.3 L5.3 32.7\"\/>\n          <text class=\"tx a-fade\" style=\"--d:1.45s\" x=\"-0.5\" y=\"29.4\" text-anchor=\"end\">12u<\/text>\n          \n          <path class=\"ln dx a-fade\" style=\"--d:1.25s\" d=\"M19 60 V31\"\/>\n          <path class=\"ln dm a-draw\" pathLength=\"1\" style=\"--d:1.3s;--t:.5s\" d=\"M19 38 A22 22 0 0 1 34.556 44.444\"\/>\n          <text class=\"tx a-fade\" style=\"--d:1.55s\" x=\"16.5\" y=\"41.5\" text-anchor=\"end\">45\u00b0<\/text>\n          \n          <path class=\"ln dm a-draw\" pathLength=\"1\" style=\"--d:1.4s;--t:.35s\" d=\"M85 76 L92 83\"\/>\n          <path class=\"ln dm a-draw\" pathLength=\"1\" style=\"--d:1.5s;--t:.45s\" d=\"M88.5 79.5 V114 H92\"\/>\n          <text class=\"tx a-fade\" style=\"--d:1.7s\" x=\"93.5\" y=\"115.2\">tail stands off<\/text>\n        <\/g>\n\n        \n        <g class=\"qc-bp-flat\" clip-path=\"url(#qcbp-clip)\">\n          <g fill=\"none\" stroke-width=\"12\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n            <g transform=\"translate(6 -2.72)\" stroke=\"#10151F\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#283805\"\/><\/g>\n            <g transform=\"translate(5.25 -2.38)\" stroke=\"#131A26\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#2F4206\"\/><\/g>\n            <g transform=\"translate(4.5 -2.04)\" stroke=\"#171E2C\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#374D08\"\/><\/g>\n            <g transform=\"translate(3.75 -1.7)\" stroke=\"#1B2333\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#40590A\"\/><\/g>\n            <g transform=\"translate(3 -1.36)\" stroke=\"#1F2839\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#49650C\"\/><\/g>\n            <g transform=\"translate(2.25 -1.02)\" stroke=\"#242E40\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#53720E\"\/><\/g>\n            <g transform=\"translate(1.5 -.68)\" stroke=\"#2A3447\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#5E8010\"\/><\/g>\n            <g transform=\"translate(.75 -.34)\" stroke=\"#323D52\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#6E9400\"\/><\/g>\n            <g stroke=\"#F6F5F0\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\" stroke=\"#C8F53B\"\/><\/g>\n          <\/g>\n          <g class=\"icl\"><use href=\"#qcbp-pl\"\/><use href=\"#qcbp-pr\"\/><use href=\"#qcbp-pt\"\/><\/g>\n        <\/g>\n\n        <g class=\"qc-bp-nodes\">\n          <circle class=\"nd a-pop\" style=\"--d:.3s\"  cx=\"51\"  cy=\"28\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.38s\" cx=\"19\"  cy=\"60\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.46s\" cx=\"51\"  cy=\"92\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.42s\" cx=\"69\"  cy=\"28\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.5s\"  cx=\"101\" cy=\"60\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.58s\" cx=\"69\"  cy=\"92\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.66s\" cx=\"97\"  cy=\"88\"  r=\"1.5\"\/>\n          <circle class=\"nd a-pop\" style=\"--d:.72s\" cx=\"110\" cy=\"101\" r=\"1.5\"\/>\n        <\/g>\n      <\/svg>\n    <\/div>\n    <canvas class=\"qc-bp-canvas\" aria-hidden=\"true\"><\/canvas>\n  <\/div>\n\n  <div class=\"qc-bp-main\">\n    <div class=\"qc-bp-copy\">\n      <p class=\"qc-bp-label\"><span class=\"qc-bp-type\"><span aria-hidden=\"true\">\/\/ <\/span>Hello, World<\/span><\/p>\n      <div class=\"qc-bp-h1\" id=\"qc-bp-h1\" role=\"heading\" aria-level=\"1\">\n        <span class=\"qc-bp-line\" style=\"--d:.25s\"><span class=\"qc-bp-line-in\">Code that runs<\/span><\/span>\n        <span class=\"qc-bp-line\" style=\"--d:.58s\"><span class=\"qc-bp-line-in\">your <span class=\"qc-bp-nowrap\">business.<span class=\"qc-bp-cursor\" aria-hidden=\"true\"><\/span><\/span><\/span><\/span>\n      <\/div>\n      <p class=\"qc-bp-lede\">Custom software for your business: websites, online stores, apps and business systems, for companies of any size, anywhere. We build them around how you work, so they help you save time, make fewer mistakes and sell more.<\/p>\n      <div class=\"qc-bp-ctas\">\n        <a class=\"qc-bp-btn\" href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.\" target=\"_blank\" rel=\"noopener\"><span>Message us on WhatsApp<\/span><span class=\"qc-bp-sr\"> (opens WhatsApp in a new tab)<\/span><svg viewBox=\"0 0 14 14\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M4.5 1.5 L10 7 L4.5 12.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/a>\n        <a class=\"qc-bp-link\" href=\"#qc-bp-build\"><span>See what we build<\/span><svg viewBox=\"0 0 12 12\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M1.5 4 L6 8.5 L10.5 4\" fill=\"none\" stroke=\"#C8F53B\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/a>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"qc-bp-tb\">\n    \n    <div class=\"qc-bp-tb-head\" aria-hidden=\"true\"><span>qcoders.com<\/span> <span>Sheet 01 \/ 02<\/span><\/div>\n    <dl class=\"qc-bp-tb-grid\">\n      <dt aria-hidden=\"true\">Drawing<\/dt><dd aria-hidden=\"true\">The mark, 1 : 1<\/dd>\n      <dt>View<\/dt>\n      <dd><span class=\"qc-bp-segs\" role=\"group\" aria-label=\"Show the mark as\">\n        <button type=\"button\" class=\"qc-bp-seg\" data-view=\"blueprint\" aria-pressed=\"false\">Blueprint<\/button>\n        <button type=\"button\" class=\"qc-bp-seg\" data-view=\"solid\" aria-pressed=\"true\">Solid<\/button>\n      <\/span><\/dd>\n      <dt aria-hidden=\"true\">Status<\/dt><dd aria-hidden=\"true\"><span class=\"qc-bp-status\" data-status=\"running\"><i aria-hidden=\"true\"><\/i><span class=\"qc-bp-status-t\">Running<\/span><\/span><\/dd>\n    <\/dl>\n  <\/div>\n  <div class=\"qc-bp-scroll\" aria-hidden=\"true\"><i><\/i><span>Scroll to build<\/span><\/div>\n  <div class=\"qc-bp-cross\" aria-hidden=\"true\"><i class=\"qc-bp-cross-h\"><\/i><i class=\"qc-bp-cross-v\"><\/i><i class=\"qc-bp-cross-s\"><\/i><span class=\"qc-bp-cross-r\"><\/span><\/div>\n<\/section>\n\n\n<section class=\"qc-bp-build\" id=\"qc-bp-build\" aria-labelledby=\"qc-bp-h2\">\n  <div class=\"qc-bp-pin\">\n    <div class=\"qc-bp-sheet\" aria-hidden=\"true\"><\/div>\n\n    <div class=\"qc-bp-copy2\">\n      <p class=\"qc-bp-label\"><span aria-hidden=\"true\">\/\/ <\/span>What we build<\/p>\n      <h2 class=\"qc-bp-h2\" id=\"qc-bp-h2\">From idea to running system.<\/h2>\n      <p class=\"qc-bp-intro\">Custom software for your business, in 3 layers built to work together. Your customers use the website and the app. Meanwhile, your team runs the business from the system underneath.<\/p>\n      <ol class=\"qc-bp-parts\" role=\"list\">\n        <li class=\"qc-bp-part\"><span class=\"qc-bp-part-n\" aria-hidden=\"true\">01<\/span><div><h3 class=\"qc-bp-part-t\">Website<\/h3><div class=\"qc-bp-part-dw\"><div><p class=\"qc-bp-part-d\">A website or online store that explains what you do and makes it easy to buy or contact you.<\/p><\/div><\/div><\/div><i class=\"qc-bp-part-bar\" aria-hidden=\"true\"><\/i><\/li>\n        <li class=\"qc-bp-part\"><span class=\"qc-bp-part-n\" aria-hidden=\"true\">02<\/span><div><h3 class=\"qc-bp-part-t\">App<\/h3><div class=\"qc-bp-part-dw\"><div><p class=\"qc-bp-part-d\">Android and iPhone apps that put your service on your customer's phone, when your project really needs one.<\/p><\/div><\/div><\/div><i class=\"qc-bp-part-bar\" aria-hidden=\"true\"><\/i><\/li>\n        <li class=\"qc-bp-part\"><span class=\"qc-bp-part-n\" aria-hidden=\"true\">03<\/span><div><h3 class=\"qc-bp-part-t\">System<\/h3><div class=\"qc-bp-part-dw\"><div><p class=\"qc-bp-part-d\">Customers, sales, inventory, staff and bookings in one system, instead of scattered spreadsheets.<\/p><\/div><\/div><\/div><i class=\"qc-bp-part-bar\" aria-hidden=\"true\"><\/i><\/li>\n      <\/ol>\n      <p class=\"qc-bp-conn\"><i aria-hidden=\"true\"><\/i>Connected, so data moves by itself.<\/p>\n      <a class=\"qc-bp-link\" href=\"\/services\/\"><span>See all 12 services<\/span><svg viewBox=\"0 0 12 12\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M4 1.5 L8.5 6 L4 10.5\" fill=\"none\" stroke=\"#C8F53B\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><\/a>\n    <\/div>\n\n    <div class=\"qc-bp-stage2\">\n      <div class=\"qc-bp-scene\" aria-hidden=\"true\">\n        <div class=\"qc-bp-iso\">\n          <div class=\"qc-bp-floor\"><\/div>\n\n          \n          <div class=\"qc-bp-plate\" data-i=\"2\" style=\"--i:2\">\n            <div class=\"qc-bp-side qc-bp-side--b\"><\/div><div class=\"qc-bp-side qc-bp-side--l\"><\/div>\n            <div class=\"qc-bp-face\">\n              <div class=\"qc-bp-ui qc-bp-ui--real qc-ui-sys\">\n                <div class=\"s-side\"><i class=\"s-logo\"><\/i><span class=\"on\"><\/span><span><\/span><span><\/span><span><\/span><span><\/span><\/div>\n                <div class=\"s-main\">\n                  <div class=\"s-top\"><span class=\"s-title\">Overview<\/span><i class=\"s-search\"><\/i><\/div>\n                  <div class=\"s-kpis\">\n                    <div class=\"s-kpi\"><span>Orders<\/span><b style=\"--v:72%\"><\/b><\/div>\n                    <div class=\"s-kpi\"><span>Stock<\/span><b style=\"--v:54%\"><\/b><\/div>\n                    <div class=\"s-kpi\"><span>Customers<\/span><b style=\"--v:86%\"><\/b><\/div>\n                  <\/div>\n                  <div class=\"s-row2\">\n                    <div class=\"s-chart\"><i style=\"--h:.38\"><\/i><i style=\"--h:.52\"><\/i><i style=\"--h:.44\"><\/i><i style=\"--h:.63\"><\/i><i style=\"--h:.57\"><\/i><i style=\"--h:.72\"><\/i><i style=\"--h:.66\"><\/i><i style=\"--h:.81\"><\/i><i style=\"--h:.75\"><\/i><i style=\"--h:.94\"><\/i><\/div>\n                    <div class=\"s-list\"><span><\/span><span><\/span><span><\/span><span><\/span><span><\/span><\/div>\n                  <\/div>\n                <\/div>\n              <\/div>\n              <i class=\"qc-bp-scan\"><\/i><i class=\"qc-bp-ticks\"><\/i>\n            <\/div>\n          <\/div>\n\n          \n          <div class=\"qc-bp-plate\" data-i=\"1\" style=\"--i:1\">\n            <div class=\"qc-bp-side qc-bp-side--b\"><\/div><div class=\"qc-bp-side qc-bp-side--l\"><\/div>\n            <div class=\"qc-bp-face\">\n              <div class=\"qc-bp-ui qc-bp-ui--real qc-ui-app\">\n                <div class=\"a-ph\">\n                  <div class=\"a-top\"><span class=\"a-title\">Orders<\/span><i class=\"a-av\"><\/i><\/div>\n                  <div class=\"a-pills\"><i><\/i><i><\/i><i><\/i><\/div>\n                  <div class=\"a-row\"><i class=\"qc-img\"><\/i><span><\/span><b class=\"on\"><\/b><\/div>\n                  <div class=\"a-row\"><i class=\"qc-img\"><\/i><span><\/span><b><\/b><\/div>\n                  <div class=\"a-row\"><i class=\"qc-img\"><\/i><span><\/span><b><\/b><\/div>\n                  <div class=\"a-row\"><i class=\"qc-img\"><\/i><span><\/span><b class=\"on\"><\/b><\/div>\n                  <div class=\"a-tab\"><i class=\"on\"><\/i><i><\/i><i><\/i><i><\/i><\/div>\n                <\/div>\n                <div class=\"a-ph\">\n                  <i class=\"qc-img a-hero\"><\/i>\n                  <span class=\"a-h\">Product<\/span>\n                  <i class=\"a-line\"><\/i><i class=\"a-line a-line--s\"><\/i>\n                  <span class=\"a-btn\">Add to cart<\/span>\n                <\/div>\n                <div class=\"a-ph\">\n                  <div class=\"a-top\"><span class=\"a-title\">Booking<\/span><i class=\"a-av\"><\/i><\/div>\n                  <div class=\"a-cal\"><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i class=\"on\"><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><i><\/i><\/div>\n                  <div class=\"a-slots\"><i><\/i><i class=\"on\"><\/i><i><\/i><i><\/i><\/div>\n                  <span class=\"a-btn\">Confirm<\/span>\n                <\/div>\n              <\/div>\n              <i class=\"qc-bp-scan\"><\/i><i class=\"qc-bp-ticks\"><\/i>\n            <\/div>\n          <\/div>\n\n          \n          <div class=\"qc-bp-plate\" data-i=\"0\" style=\"--i:0\">\n            <div class=\"qc-bp-side qc-bp-side--b\"><\/div><div class=\"qc-bp-side qc-bp-side--l\"><\/div>\n            <div class=\"qc-bp-face\">\n              <div class=\"qc-bp-ui qc-bp-ui--real qc-ui-web\">\n                <div class=\"w-bar\"><i><\/i><i><\/i><i><\/i><span class=\"w-url\">yourbusiness.com<\/span><\/div>\n                <div class=\"w-nav\"><i class=\"w-logo\"><\/i><span class=\"w-links\"><span>Home<\/span> <span>Products<\/span> <span>Contact<\/span><\/span> <span class=\"w-cta\">Order<\/span><\/div>\n                <div class=\"w-hero\">\n                  <div class=\"w-copy\"><span class=\"w-h\">Your business, online.<\/span><i class=\"w-p\"><\/i><i class=\"w-p w-p--s\"><\/i> <span class=\"w-btn\">Shop now<\/span><\/div>\n                  <i class=\"qc-img w-img\"><\/i>\n                <\/div>\n                <div class=\"w-cards\">\n                  <div class=\"w-card\"><i class=\"qc-img\"><\/i><span><\/span><span><\/span><\/div>\n                  <div class=\"w-card\"><i class=\"qc-img\"><\/i><span><\/span><span><\/span><\/div>\n                  <div class=\"w-card\"><i class=\"qc-img\"><\/i><span><\/span><span><\/span><\/div>\n                <\/div>\n              <\/div>\n              <i class=\"qc-bp-scan\"><\/i><i class=\"qc-bp-ticks\"><\/i>\n            <\/div>\n          <\/div>\n\n          \n          <i class=\"qc-bp-guide\" style=\"left:14%;top:16%\"><b class=\"qc-bp-packet\"><\/b><\/i>\n          <i class=\"qc-bp-guide\" style=\"left:84%;top:80%\"><b class=\"qc-bp-packet qc-bp-packet--down\" style=\"--pd:-.9s\"><\/b><\/i>\n          <i class=\"qc-bp-guide\" style=\"left:16%;top:82%\"><b class=\"qc-bp-packet\" style=\"--pd:-1.5s\"><\/b><\/i>\n        <\/div>\n\n        <svg class=\"qc-bp-callouts\" focusable=\"false\">\n          <g class=\"qc-bp-co\"><path class=\"lead\"\/><circle class=\"dot\" r=\"2.5\"\/><g class=\"bl\"><circle class=\"ball\" r=\"13\"\/><text class=\"num\">01<\/text><text class=\"name\" x=\"22\">Website<\/text><\/g><\/g>\n          <g class=\"qc-bp-co\"><path class=\"lead\"\/><circle class=\"dot\" r=\"2.5\"\/><g class=\"bl\"><circle class=\"ball\" r=\"13\"\/><text class=\"num\">02<\/text><text class=\"name\" x=\"22\">App<\/text><\/g><\/g>\n          <g class=\"qc-bp-co\"><path class=\"lead\"\/><circle class=\"dot\" r=\"2.5\"\/><g class=\"bl\"><circle class=\"ball\" r=\"13\"\/><text class=\"num\">03<\/text><text class=\"name\" x=\"22\">System<\/text><\/g><\/g>\n        <\/svg>\n      <\/div>\n\n      <div class=\"qc-bp-tb\" aria-hidden=\"true\">\n        <div class=\"qc-bp-tb-head\"><span>Exploded view<\/span> <span>Sheet 02 \/ 02<\/span><\/div>\n        <dl class=\"qc-bp-tb-grid\">\n          <dt>Layers<\/dt><dd>Website, app, system<\/dd>\n          <dt>Status<\/dt><dd><span class=\"qc-bp-status qc-bp-status--b\" data-status=\"running\"><i><\/i><span class=\"qc-bp-status-t\">Running<\/span><\/span><\/dd>\n        <\/dl>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n<\/div>\n\n<script type=\"importmap\">\n{ \"imports\": {\n  \"three\": \"https:\/\/cdn.jsdelivr.net\/npm\/three@0.160.0\/build\/three.module.min.js\",\n  \"three\/addons\/\": \"https:\/\/cdn.jsdelivr.net\/npm\/three@0.160.0\/examples\/jsm\/\"\n} }\n<\/script>\n<script data-cfasync=\"false\">\n\/* GSAP 3.12.5 + ScrollTrigger, only where this hero can show: 768px and up (on phones the section is hidden, and\n   the phone home needs neither). Added once per page, in order (async=false), so a script that loads before the\n   page's load event still delays it; the showcase reuses them. Fires \"qcgsap\" on window once ScrollTrigger has\n   run, or failed to load. A window that grows to 768px loads them then. *\/\n(function (w, d) {\n  if (w.__qcGsapLoader) return;\n  w.__qcGsapLoader = true;\n  var mq = w.matchMedia ? w.matchMedia('(min-width: 768px)') : null;\n  function load() {\n    if (w.__qcGsapLoading || (w.gsap && w.ScrollTrigger)) return;\n    w.__qcGsapLoading = true;\n    var base = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.12.5\/';\n    ['gsap.min.js', 'ScrollTrigger.min.js'].forEach(function (f, i) {\n      var s = d.createElement('script');\n      s.src = base + f; s.async = false; s.setAttribute('data-cfasync', 'false');\n      if (i === 1) s.onload = s.onerror = function () { w.dispatchEvent(new Event('qcgsap')); };\n      (d.head || d.documentElement).appendChild(s);\n    });\n  }\n  if (!mq || mq.matches) load();\n  else if (mq.addEventListener) mq.addEventListener('change', function (e) { if (e.matches) load(); });\n})(window, document);\n<\/script>\n<script type=\"module\" async data-cfasync=\"false\">\n\/* Qcoders \u00b7 Blueprint hero \u00b7 scroll + 3D layer (production).\n   async: runs as soon as it is parsed (layout, 3D decision, three.js request) and waits for GSAP itself (whenGsap).\n   Optional: GSAP 3.12.5 + ScrollTrigger (window.gsap), three@0.160.0 (ES module). Without them it shows a finished static state.\n   Safe to run more than once: each root boots once, and an instance whose markup was removed (Elementor editor re-render) is torn down. *\/\nconst REG = (window.__qcbp = window.__qcbp || []);\nfunction bootAll() {\n  for (let i = REG.length - 1; i >= 0; i--) if (!REG[i].root.isConnected) REG.splice(i, 1)[0].destroy();\n  document.querySelectorAll('[data-qcbp]:not([data-qcbp-ready])').forEach((root) => {\n    if (!root.getClientRects().length) return;      \/\/ hidden (phones: the phone home shows instead): nothing to run\n    root.setAttribute('data-qcbp-ready', '');\n    try { REG.push(boot(root)); }\n    catch (err) {\n      console.error('[qcbp] init failed, showing the static version', err);\n      root.classList.remove('qc-js'); root.classList.add('qc-bp--still');\n    }\n  });\n}\nbootAll();\nif (!window.__qcbpWatch && window.matchMedia) {         \/\/ a window that grows past the phone layout boots it then\n  window.__qcbpWatch = true;\n  const shown = window.matchMedia('(min-width: 768px)');\n  if (shown.addEventListener) shown.addEventListener('change', (e) => { if (e.matches) requestAnimationFrame(bootAll); });\n}\n\nfunction inElementorEditor() {\n  try {\n    const ef = window.elementorFrontend;\n    if (ef && typeof ef.isEditMode === 'function' && ef.isEditMode()) return true;\n  } catch (e) { \/* elementorFrontend not configured yet *\/ }\n  return \/[?&]elementor-preview=\/.test(location.search) || document.body.classList.contains('elementor-editor-active');\n}\n\nfunction boot(root) {\n  const $ = (s, el = root) => el.querySelector(s);\n  const $$ = (s, el = root) => Array.from(el.querySelectorAll(s));\n  const mq = (q) => window.matchMedia(q).matches;\n  const EDITOR = inElementorEditor();\n  const REDUCE = mq('(prefers-reduced-motion: reduce)');\n  const STILL = REDUCE || EDITOR;                      \/\/ finished frame: no intro, no pin, no WebGL download\n  const FINE = !EDITOR && mq('(hover: hover) and (pointer: fine)');\n  const STACKED_Q = '(max-width: 760px) and (min-height: 501px), (max-width: 1100px) and (orientation: portrait), (max-width: 520px)';\n  const stacked = () => mq(STACKED_Q);                 \/\/ same query as the CSS\n  const SHORT = mq('(orientation: landscape) and (max-height: 500px)');   \/\/ phones held sideways: no pin, static section 2\n  const compact = () => stacked() || mq('(min-width: 761px) and (max-height: 820px)');   \/\/ parts collapse to the active one (CSS)\n  const phone = () => window.innerWidth <= 760;\n  const clamp = (v, a = 0, b = 1) => Math.min(b, Math.max(a, v));\n  const lerp = (a, b, t) => a + (b - a) * t;\n  const seg = (p, a, b) => clamp((p - a) \/ (b - a));\n  const ease = (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) \/ 2);\n  const T0 = performance.now();\n  const GSAP = () => window.gsap && window.ScrollTrigger;\n\n  \/* ---------------------------------------------------------- lifecycle *\/\n  const undo = [];\n  let dead = false;\n  const on = (t, type, fn, opts) => { t.addEventListener(type, fn, opts); undo.push(() => t.removeEventListener(type, fn, opts)); };\n  const later = (fn, ms) => { const id = setTimeout(() => { if (alive()) fn(); }, ms); undo.push(() => clearTimeout(id)); };\n  function destroy() { if (dead) return; dead = true; undo.splice(0).reverse().forEach((f) => { try { f(); } catch (e) { \/* already gone *\/ } }); }\n  function alive() { if (root.isConnected) return true; destroy(); return false; }\n  if (EDITOR) { root.classList.remove('qc-js'); root.classList.add('qc-bp--still'); }\n\n  \/* ------------------------------------------------- sticky site header *\/\n  \/\/ Hero height and the pin offset follow the header's real bottom edge: Elementor Pro's sticky\n  \/\/ header (fixed + spacer once active), CSS-sticky headers, and the WP admin bar (32px, or 46px on narrow screens).\n  function readHeader() {\n    const hdr = document.querySelector('.elementor-location-header');\n    if (!hdr || hdr.contains(root)) return null;\n    const stuck = hdr.querySelector('.elementor-sticky--active');\n    const r = (stuck || hdr).getBoundingClientRect();\n    if (r.height < 1) return null;\n    const bar = document.getElementById('wpadminbar');\n    const barH = bar && getComputedStyle(bar).position === 'fixed' ? Math.round(bar.getBoundingClientRect().height) : 0;\n    const sticky = !!stuck || \/sticky|fixed\/.test(getComputedStyle(hdr).position) || !!hdr.querySelector('.elementor-sticky, [data-settings*=\"sticky\"]');\n    const bottom = Math.round(stuck ? r.bottom : barH + r.height);   \/\/ where it sits while the page scrolls\n    return { fit: bottom, pin: sticky ? bottom : barH };\n  }\n  let hdrKey = 'css';\n  function syncHeader() {\n    const h = readHeader();\n    const key = h ? `${h.fit}\/${h.pin}` : 'css';\n    if (key === hdrKey) return false;\n    hdrKey = key;\n    if (h) { root.style.setProperty('--qc-header-h', h.fit + 'px'); root.style.setProperty('--qc-pin-top', h.pin + 'px'); }\n    else { root.style.removeProperty('--qc-header-h'); root.style.removeProperty('--qc-pin-top'); }\n    return true;\n  }\n  const pinTop = () => parseFloat(getComputedStyle(root).getPropertyValue('--qc-pin-top')) || 0;\n  syncHeader();\n\n  \/* ----------------------------------------------------------- HERO *\/\n  const hero = $('.qc-bp-hero');\n  const stage = $('.qc-bp-stage');\n  const copy = $('.qc-bp-copy');\n  const draw = $('.qc-bp-draw');\n  const canvas = $('.qc-bp-canvas');\n  const heroStatus = $('.qc-bp-hero .qc-bp-status');\n  const segs = $$('.qc-bp-seg');\n  const tb = $('.qc-bp-hero .qc-bp-tb');\n  const MARK = { cx: 64.5, cy: 64.5, w: 115, h: 97 };\n  \/\/ The drawing incl. dimension labels and the tilted 3D solid, in mark units around the mark's centre:\n  \/\/ 130u wide with its middle 5u left of the mark's, 62u above and 58u below.\n  const DRAWING = { w: 130, shift: 5, up: 62, down: 58 };\n  const view = { s: 4.6, W: 1, H: 1, ox: 0, oy: 0 };\n  const DRAW_MS = 1600, GIVE_UP_MS = 3300;\n  let mode = 'pending';          \/\/ pending | 3d | flat\n  let mark3d = null;\n  let heroExit = 0;\n  let layoutKey = '';\n\n  function setStatus(el, key, label) {\n    if (!el || el.dataset.status === key && el.textContent.trim() === label) return;\n    el.dataset.status = key;\n    el.querySelector('.qc-bp-status-t').textContent = label;\n  }\n\n  \/\/ Desktop: the drawing sits in the free space right of the copy, level with the copy and clear of the\n  \/\/ title block, so it never collides with either whatever the content width or screen height.\n  \/\/ Phone: centred in the stage above the copy.\n  function layoutHero() {\n    const W = stage.clientWidth, H = stage.clientHeight;\n    if (!W || !H) return;\n    let s, ox = 0, oy = 0;\n    if (stacked()) {\n      s = Math.min((H * 0.6) \/ MARK.h, (W * 0.64) \/ MARK.w);\n    } else {\n      const hr = hero.getBoundingClientRect(), cr = copy.getBoundingClientRect(), tr = tb.getBoundingClientRect();\n      const gut = cr.left - hr.left;\n      const L = Math.max(0, cr.right - hr.left + 32 - (hr.width - W)), R = W - Math.max(48, gut * 0.4);\n      const free = Math.max(R - L, W * 0.45);\n      const top = 28, bottom = (tr.height ? tr.top - hr.top : hr.height) - 16;   \/\/ title block hidden on phones held sideways\n      s = Math.min((H * 0.52) \/ MARK.h, (free * 0.94) \/ DRAWING.w, (bottom - top) \/ (DRAWING.up + DRAWING.down));\n      const cy = clamp(cr.top - hr.top + cr.height \/ 2, top + DRAWING.up * s, bottom - DRAWING.down * s);\n      ox = (L + R) \/ 2 + DRAWING.shift * s - W \/ 2;\n      oy = cy - H \/ 2;\n    }\n    const key = `${W}|${H}|${s.toFixed(3)}|${ox.toFixed(1)}|${oy.toFixed(1)}`;\n    if (key === layoutKey) { if (mark3d) mark3d.redraw(); return; }   \/\/ same size: just a fresh frame, no canvas reallocation\n    layoutKey = key;\n    Object.assign(view, { s, W, H, ox, oy });\n    const vx = MARK.cx - (W \/ 2 + ox) \/ s, vy = MARK.cy - (H \/ 2 + oy) \/ s;\n    draw.setAttribute('viewBox', `${vx.toFixed(3)} ${vy.toFixed(3)} ${(W \/ s).toFixed(3)} ${(H \/ s).toFixed(3)}`);\n    draw.style.setProperty('--s', s.toFixed(3));\n    if (mark3d) mark3d.resize();\n  }\n  layoutHero();\n\n  function goFlat() {\n    if (mode !== 'pending') return;\n    mode = 'flat';\n    hero.classList.add('is-flat');\n    hero.classList.toggle('is-solid', isSolidView());\n    setStatus(heroStatus, 'running', 'Running');\n    if (!STILL) snap(1300);\n  }\n  \/\/ The intro is CSS + timers. If frames were throttled (background tab, slow device),\n  \/\/ these make sure the page still lands on its final frame on time.\n  function snap(delay) {\n    later(() => { root.classList.add('is-snap'); void root.offsetWidth; later(() => root.classList.remove('is-snap'), 60); }, delay);\n  }\n  if (STILL) root.classList.add('is-settled');\n  else later(() => root.classList.add('is-settled'), 2800);\n  const isSolidView = () => segs.some((b) => b.dataset.view === 'solid' && b.getAttribute('aria-pressed') === 'true');\n\n  segs.forEach((b) => on(b, 'click', () => {\n    const solid = b.dataset.view === 'solid';\n    segs.forEach((x) => x.setAttribute('aria-pressed', String((x.dataset.view === 'solid') === solid)));\n    hero.classList.toggle('is-solid', solid);\n    if (mode === '3d' && mark3d) mark3d.setSolid(solid);\n  }));\n\n  function hasWebGL() {\n    try {\n      const c = document.createElement('canvas');\n      const gl = c.getContext('webgl2') || c.getContext('webgl');\n      if (!gl) return false;\n      const lose = gl.getExtension('WEBGL_lose_context');\n      if (lose) lose.loseContext();\n      return true;\n    } catch (e) { return false; }\n  }\n  const conn = navigator.connection || {};\n  const lowEnd = conn.saveData === true || (navigator.deviceMemory && navigator.deviceMemory <= 2) || (navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 2);\n\n  if (STILL) {\n    goFlat();\n  } else if (lowEnd || !hasWebGL()) {\n    setStatus(heroStatus, 'drafting', 'Drafting');\n    later(goFlat, DRAW_MS);\n  } else {\n    setStatus(heroStatus, 'drafting', 'Drafting');\n    const afterPaint = (fn) => requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(fn, 0)));\n    afterPaint(() => {\n      if (!alive()) return;\n      loadThree()\n        .then((ctrl) => {\n          if (!ctrl) return;\n          mark3d = ctrl;\n          later(() => {\n            if (mark3d !== ctrl) return;                 \/\/ the GPU dropped the context meanwhile: stay flat\n            if (mode === 'pending') { mode = '3d'; ctrl.intro(); }\n            else if (mode === 'flat') { mode = '3d'; ctrl.upgrade(); }\n          }, Math.max(0, T0 + DRAW_MS - performance.now()));\n        })\n        .catch(() => { if (alive()) goFlat(); });\n    });\n    later(() => { if (mode === 'pending') goFlat(); }, GIVE_UP_MS);\n  }\n\n  async function loadThree() {\n    let THREE;\n    try { THREE = await import('three'); }\n    catch (e) { THREE = await import('https:\/\/cdn.jsdelivr.net\/npm\/three@0.160.0\/build\/three.module.min.js'); }\n    await new Promise((r) => ('requestIdleCallback' in window ? requestIdleCallback(r, { timeout: 500 }) : setTimeout(r, 16)));\n    return alive() ? await createMark(THREE) : null;\n  }\n\n  \/* ------------------------------------------------ the 3D mark (three.js) *\/\n  \/\/ Built in short tasks: it yields around the environment bake and compiles the shaders asynchronously,\n  \/\/ so it doesn't block input during the intro. The default GPU is enough for one decorative mark.\n  async function createMark(THREE) {\n    const mobile = stacked() || mq('(pointer: coarse)');\n    const dprCap = Math.min(window.devicePixelRatio || 1, mobile ? 1.5 : 2);\n    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });\n    const abandon = () => { renderer.dispose(); renderer.forceContextLoss(); return null; };\n    const yieldTask = () => new Promise((r) => setTimeout(r, 0));\n    renderer.setPixelRatio(dprCap);\n    renderer.setClearColor(0x000000, 0);\n    renderer.localClippingEnabled = true;\n    renderer.outputColorSpace = THREE.SRGBColorSpace;\n\n    const scene = new THREE.Scene();\n    const camera = new THREE.PerspectiveCamera(30, 1, 10, 4000);\n    await yieldTask();\n    if (!alive()) return abandon();\n    scene.environment = makeEnv(THREE, renderer);\n    await yieldTask();\n    if (!alive()) { scene.environment.dispose(); return abandon(); }\n    \/\/ r155+ lights are physical (no PI factor), hence the larger numbers\n    scene.add(new THREE.HemisphereLight(0xffffff, 0x0a0e17, 0.6));\n    const key = new THREE.DirectionalLight(0xffffff, 2.6); key.position.set(-30, 200, 60); scene.add(key);\n    const fill = new THREE.DirectionalLight(0xffffff, 2.2); fill.position.set(-90, 30, 180); scene.add(fill);\n    const rim = new THREE.DirectionalLight(0xdde6ff, 0.4); rim.position.set(160, 40, -80); scene.add(rim);\n\n    \/\/ geometry: each stroke of the mark becomes an outline shape, then an extrusion\n    const R = 6, DEPTH = 14, BT = 1.6, BS = 1.3;\n    const strokes = [\n      { pts: [[51, 28], [19, 60], [51, 92]], volt: false },\n      { pts: [[69, 28], [101, 60], [69, 92]], volt: false },\n      { pts: [[97, 88], [110, 101]], volt: true },\n    ];\n    const plane = new THREE.Plane(new THREE.Vector3(-1, 1, -1.4).normalize(), -90);\n    const Mat = mobile ? THREE.MeshStandardMaterial : THREE.MeshPhysicalMaterial;\n    const paperMat = new Mat(Object.assign({ color: 0xf6f5f0, roughness: 0.34, metalness: 0, envMapIntensity: 0.6, clippingPlanes: [plane] }, mobile ? {} : { clearcoat: 0.5, clearcoatRoughness: 0.25 }));\n    const voltMat = new THREE.MeshStandardMaterial({ color: 0xc8f53b, roughness: 0.4, metalness: 0, emissive: 0xc8f53b, emissiveIntensity: 0.05, envMapIntensity: 0.35, clippingPlanes: [plane] });\n    const capMat = new THREE.MeshBasicMaterial({ color: 0xc8f53b, side: THREE.BackSide, clippingPlanes: [plane] });\n    const edgeMat = new THREE.LineBasicMaterial({ color: 0xf6f5f0, transparent: true, opacity: 0, depthWrite: false });\n\n    const group = new THREE.Group(), pivot = new THREE.Group();\n    group.add(pivot); scene.add(group);\n    const caps = [];\n    for (const s of strokes) {\n      const shape = strokeShape(THREE, s.pts, R);\n      const g = new THREE.ExtrudeGeometry(shape, { depth: DEPTH, bevelEnabled: true, bevelThickness: BT, bevelSize: BS, bevelOffset: -BS, bevelSegments: mobile ? 2 : 4, curveSegments: mobile ? 10 : 16 });\n      g.translate(0, 0, -(DEPTH + BT));\n      const cap = new THREE.Mesh(g, capMat);\n      caps.push(cap);\n      pivot.add(cap, new THREE.Mesh(g, s.volt ? voltMat : paperMat));\n      const eg = new THREE.ExtrudeGeometry(shape, { depth: DEPTH + 2 * BT, bevelEnabled: false, curveSegments: 16 });\n      eg.translate(0, 0, -(DEPTH + 2 * BT));\n      pivot.add(new THREE.LineSegments(new THREE.EdgesGeometry(eg, 25), edgeMat));\n      eg.dispose();\n    }\n\n    \/\/ tiny tween engine (no GSAP needed for the hero)\n    const st = { extrude: 0.002, pose: 0, solid: 0, edges: 0 };\n    let tweens = [];\n    function tween(k, to, dur, delay = 0) { tweens.push({ k, to, dur, at: performance.now() + delay, from: null }); wake(); }\n    function clearTweens(keys) { tweens = tweens.filter((t) => !keys.includes(t.k)); }\n    function runTweens(now) {\n      for (const t of tweens) {\n        if (now < t.at) continue;\n        if (t.from === null) { tweens = tweens.filter((o) => o === t || o.k !== t.k || o.from === null); t.from = st[t.k]; }\n        const k = clamp((now - t.at) \/ t.dur);\n        st[t.k] = t.from + (t.to - t.from) * ease(k);\n        t.done = k >= 1;\n      }\n      tweens = tweens.filter((t) => !t.done);\n    }\n\n    const ptr = { x: 0, y: 0, tx: 0, ty: 0 };\n    if (FINE) on(window, 'pointermove', (e) => { ptr.tx = (e.clientX \/ window.innerWidth) * 2 - 1; ptr.ty = (e.clientY \/ window.innerHeight) * 2 - 1; wake(); }, { passive: true });\n\n    \/\/ The idle float fades out 4-5 s after load, so the mark comes to rest (nothing moves on its own for more than 5 s).\n    const floatAmt = (now) => clamp(1 - (now - T0 - 4000) \/ 1000);\n    function update(now) {\n      runTweens(now);\n      const t = now \/ 1000;\n      ptr.x += (ptr.tx - ptr.x) * 0.05; ptr.y += (ptr.ty - ptr.y) * 0.05;\n      const p = st.pose, f = floatAmt(now) * p;\n      let ry = (-0.42 + ptr.x * 0.26) * p + Math.sin(t * 0.32) * 0.05 * f;\n      let rx = (0.2 + ptr.y * 0.16) * p + Math.sin(t * 0.45) * 0.025 * f;\n      let rz = 0;\n      const e = ease(clamp(heroExit * 1.2));\n      rx = lerp(rx, -0.96, e); ry = lerp(ry, 0, e); rz = lerp(rz, 0.785, e);\n      group.rotation.set(rx, ry, rz);\n      group.position.y = Math.sin(t * 0.7) * 1.1 * f + e * 4;\n      group.scale.setScalar(1 - e * 0.14);\n      pivot.scale.z = Math.max(0.002, st.extrude);\n      plane.constant = lerp(-90, 90, st.solid);\n      edgeMat.opacity = st.edges * 0.75;\n      edgeMat.visible = st.edges > 0.001;\n      for (const c of caps) c.visible = st.solid < 0.999;\n    }\n\n    \/\/ Render loop: frames only while something moves (intro tweens, the fading idle float, pointer parallax\n    \/\/ settling, the hero scrolling out), the hero is on screen and the tab is visible. Then it stops until wake().\n    \/\/ Phones draw at most 30 fps, at any refresh rate, while only the float moves.\n    const PHONE_MS = 1000 \/ 30 - 2, IDLE_MS = 300;     \/\/ the float ends, so frames after motion stops are identical\n    let raf = 0, onScreen = true, ready = false, lost = false, lastExit = -1, lastDraw = 0, lastMove = performance.now();\n    const frame = (now) => {\n      raf = 0;\n      if (!alive() || lost) return;\n      const busy = tweens.length > 0 || heroExit !== lastExit;\n      const settling = Math.abs(ptr.tx - ptr.x) + Math.abs(ptr.ty - ptr.y) > 1e-3;\n      if (busy || settling || floatAmt(now) * st.pose > 0) lastMove = now;\n      else if (now - lastMove > IDLE_MS) return;       \/\/ idle: no frames until wake()\n      raf = requestAnimationFrame(frame);\n      if (mobile && !busy && now - lastDraw < PHONE_MS) return;\n      lastDraw = now; lastExit = heroExit;\n      update(now); renderer.render(scene, camera);\n    };\n    function wake() { if (ready && !lost && !raf && onScreen && !document.hidden) raf = requestAnimationFrame(frame); }\n    function sleep() { if (raf) cancelAnimationFrame(raf); raf = 0; }\n    const io = new IntersectionObserver(([en]) => { onScreen = en.isIntersecting; onScreen ? wake() : sleep(); }, { rootMargin: '80px' });\n    io.observe(hero);\n    on(document, 'visibilitychange', () => (document.hidden ? sleep() : wake()));\n    undo.push(() => {\n      sleep(); io.disconnect();\n      scene.traverse((o) => { if (o.geometry) o.geometry.dispose(); });\n      [paperMat, voltMat, capMat, edgeMat].forEach((m) => m.dispose());\n      if (scene.environment) scene.environment.dispose();\n      renderer.dispose(); if (!lost) renderer.forceContextLoss();\n    });\n    \/\/ GPU reset, memory pressure or a backgrounded phone dropped the context: show the flat mark, not an empty canvas\n    on(canvas, 'webglcontextlost', () => {\n      if (dead || lost) return;\n      lost = true; sleep(); mark3d = null; mode = 'flat';\n      hero.classList.remove('is-3d'); hero.classList.add('is-flat');\n      hero.classList.toggle('is-solid', isSolidView());\n      setStatus(heroStatus, 'running', 'Running');\n    });\n\n    \/\/ Compile the shaders before the first frame: off the main thread where the browser allows it\n    \/\/ (KHR_parallel_shader_compile, which compileAsync waits on), otherwise start them here and yield once.\n    try {\n      if (renderer.compileAsync && renderer.getContext().getExtension('KHR_parallel_shader_compile')) await renderer.compileAsync(scene, camera);\n      else { renderer.compile(scene, camera); await yieldTask(); }\n    } catch (e) { \/* compiles on the first render instead *\/ }\n    if (!alive() || lost) return null;\n    ready = true;\n\n    function resize() {\n      const { W, H, s, ox, oy } = view;\n      \/\/ cap the backing store at about 2.5 MP (large desktop stages at DPR 2), never below the device's own DPR or 1\n      const pr = Math.min(dprCap, Math.max(1, Math.sqrt(2.5e6 \/ (W * H))));\n      if (renderer.getPixelRatio() !== pr) renderer.setPixelRatio(pr);\n      renderer.setSize(W, H, false);\n      const D = H \/ s \/ (2 * Math.tan(THREE.MathUtils.degToRad(15)));\n      camera.aspect = W \/ H;\n      camera.position.set(0, 0, D); camera.lookAt(0, 0, 0);\n      camera.near = D * 0.3; camera.far = D * 3;\n      camera.setViewOffset(W, H, -ox, -oy, W, H);\n      camera.updateProjectionMatrix();\n      redraw();\n    }\n    function redraw() { update(performance.now()); renderer.render(scene, camera); }\n    resize();\n\n    return {\n      resize,\n      redraw,\n      wake,\n      intro() {\n        hero.classList.add('is-3d');\n        setStatus(heroStatus, 'building', 'Building');\n        tween('edges', 1, 250, 0);\n        tween('extrude', 1, 1000, 100);\n        tween('pose', 1, 1400, 100);\n        tween('solid', 1, 1000, 500);\n        tween('edges', 0, 550, 1300);\n        later(() => hero.classList.toggle('is-solid', isSolidView()), 900);\n        later(() => setStatus(heroStatus, 'running', 'Running'), 1700);\n        snap(1850);\n        later(redraw, 1900);   \/\/ settle frame: the tweens are time-based, so this lands on the final pose even if frames were throttled\n        if (!isSolidView()) later(() => this.setSolid(false), 1900);\n      },\n      upgrade() {   \/\/ 3D arrived after the flat fallback was already shown\n        st.solid = isSolidView() ? 1 : 0; st.edges = st.solid ? 0 : 1;\n        hero.classList.add('is-3d');\n        tween('extrude', 1, 700, 0); tween('pose', 1, 1100, 0);\n        later(() => { if (!lost) hero.classList.remove('is-flat'); }, 450);\n        snap(1000);\n        later(redraw, 1150);\n      },\n      setSolid(onOff) {\n        clearTweens(['solid', 'edges']);\n        if (onOff) { tween('solid', 1, 1100, 0); tween('edges', 0, 600, 650); }\n        else { tween('edges', 1, 350, 0); tween('solid', 0, 1000, 0); }\n      },\n    };\n  }\n\n  function strokeShape(THREE, pts, r) {\n    const V = (x, y) => new THREE.Vector2(x - MARK.cx, -(y - MARK.cy));\n    const P = pts.map(([x, y]) => V(x, y)), n = P.length;\n    const dir = [], nor = [];\n    for (let i = 0; i < n - 1; i++) { const d = P[i + 1].clone().sub(P[i]).normalize(); dir.push(d); nor.push(new THREE.Vector2(-d.y, d.x)); }\n    const ang = (v) => Math.atan2(v.y, v.x);\n    const at = (p, v, k) => p.clone().addScaledVector(v, k);\n    const sh = new THREE.Shape();\n    const s0 = at(P[0], nor[0], r); sh.moveTo(s0.x, s0.y);\n    for (let i = 1; i < n - 1; i++) {                       \/\/ left side, forward\n      const a = nor[i - 1], b = nor[i], cross = dir[i - 1].x * dir[i].y - dir[i - 1].y * dir[i].x;\n      if (cross < 0) { const e = at(P[i], a, r); sh.lineTo(e.x, e.y); sh.absarc(P[i].x, P[i].y, r, ang(a), ang(b), true); }\n      else { const m = a.clone().add(b).multiplyScalar(r \/ (1 + a.dot(b))).add(P[i]); sh.lineTo(m.x, m.y); }\n    }\n    const nl = nor[n - 2], e1 = at(P[n - 1], nl, r);\n    sh.lineTo(e1.x, e1.y); sh.absarc(P[n - 1].x, P[n - 1].y, r, ang(nl), ang(nl) - Math.PI, true);\n    for (let i = n - 2; i >= 1; i--) {                      \/\/ right side, backward\n      const a = nor[i], b = nor[i - 1], cross = dir[i - 1].x * dir[i].y - dir[i - 1].y * dir[i].x;\n      const na = a.clone().negate(), nb = b.clone().negate();\n      if (cross > 0) { const e = at(P[i], na, r); sh.lineTo(e.x, e.y); sh.absarc(P[i].x, P[i].y, r, ang(na), ang(nb), true); }\n      else { const m = na.clone().add(nb).multiplyScalar(r \/ (1 + na.dot(nb))).add(P[i]); sh.lineTo(m.x, m.y); }\n    }\n    const n0 = nor[0].clone().negate(), e0 = at(P[0], n0, r);\n    sh.lineTo(e0.x, e0.y); sh.absarc(P[0].x, P[0].y, r, ang(n0), ang(n0) - Math.PI, true);\n    sh.closePath();\n    return sh;\n  }\n\n  function makeEnv(THREE, renderer) {   \/\/ a tiny studio: dark room + three soft boxes, baked once\n    const s = new THREE.Scene();\n    s.add(new THREE.Mesh(new THREE.BoxGeometry(20, 20, 20), new THREE.MeshBasicMaterial({ color: 0x0c111c, side: THREE.BackSide })));\n    const box = (w, h, x, y, z, k) => {\n      const m = new THREE.Mesh(new THREE.PlaneGeometry(w, h), new THREE.MeshBasicMaterial({ color: new THREE.Color(k, k, k), side: THREE.DoubleSide }));\n      m.position.set(x, y, z); m.lookAt(0, 0, 0); s.add(m);\n    };\n    box(13, 2.2, 0, 8.6, 1, 3.2);      \/\/ top strip\n    box(6, 4, -4.5, 3, 7, 2.6);        \/\/ front-left box\n    box(1.4, 9, 8, 1, -4, 1.4);        \/\/ thin back-right rim\n    box(10, 2.5, 0, -7.5, 3, 0.15);    \/\/ faint floor bounce\n    const pm = new THREE.PMREMGenerator(renderer);\n    const tex = pm.fromScene(s, 0.04).texture;\n    pm.dispose();\n    s.traverse((o) => { if (o.geometry) o.geometry.dispose(); if (o.material) o.material.dispose(); });\n    return tex;\n  }\n\n  \/* crosshair + rulers: a CAD cursor (desktop pointer only). It snaps to the drawing's nodes, like a CAD tool. *\/\n  if (FINE) {\n    const cross = $('.qc-bp-cross');\n    const ch = $('.qc-bp-cross-h'), cv = $('.qc-bp-cross-v'), cr = $('.qc-bp-cross-r'), cs = $('.qc-bp-cross-s');\n    const mx = $('.qc-bp-ruler--x .qc-bp-rmark'), my = $('.qc-bp-ruler--y .qc-bp-rmark');\n    const NODES = [[51, 28], [19, 60], [51, 92], [69, 28], [101, 60], [69, 92], [97, 88], [110, 101]];\n    let px = 0, py = 0, queued = false;\n    const pad = (v) => String(Math.max(0, Math.round(v))).padStart(4, '0');\n    const paint = () => {\n      queued = false;\n      let x = px, y = py, snapped = false;\n      if (mode !== '3d' || !hero.classList.contains('is-solid')) {\n        const ox = stage.offsetLeft + view.W \/ 2 + view.ox, oy = stage.offsetTop + view.H \/ 2 + view.oy;\n        for (const [nx, ny] of NODES) {\n          const sx = ox + (nx - MARK.cx) * view.s, sy = oy + (ny - MARK.cy) * view.s;\n          if (Math.hypot(sx - px, sy - py) < 16) { x = sx; y = sy; snapped = true; break; }\n        }\n      }\n      cross.classList.toggle('is-snap', snapped);\n      ch.style.transform = `translateY(${y}px)`; cv.style.transform = `translateX(${x}px)`;\n      cs.style.transform = `translate(${x}px, ${y}px)`;\n      mx.style.transform = `translateX(${x}px)`; my.style.transform = `translateY(${y}px)`;\n      cr.style.transform = `translate(${x + 14}px, ${y + 14}px)`;\n      cr.textContent = (snapped ? 'node  ' : '') + `x ${pad(x)}  y ${pad(y)}`;\n    };\n    on(hero, 'pointermove', (e) => {\n      const r = hero.getBoundingClientRect();\n      px = e.clientX - r.left; py = e.clientY - r.top;\n      hero.classList.add('is-pointer');\n      if (!queued) { queued = true; requestAnimationFrame(paint); }\n    }, { passive: true });\n    on(hero, 'pointerleave', () => hero.classList.remove('is-pointer'));\n  }\n\n  \/* ------------------------------------------------- SECTION 2 \u00b7 exploded view *\/\n  const build = $('.qc-bp-build');\n  const pin = $('.qc-bp-pin');\n  const scene2 = $('.qc-bp-scene');\n  const iso = $('.qc-bp-iso');\n  const plates = $$('.qc-bp-plate').sort((a, b) => a.dataset.i - b.dataset.i);     \/\/ 0 website, 1 app, 2 system\n  const parts = $$('.qc-bp-part');\n  const cos = $$('.qc-bp-co');\n  const overlay = $('.qc-bp-callouts');\n  const buildStatus = $('.qc-bp-status--b');\n\n  plates.forEach((pl) => {                                   \/\/ wireframe = a stripped clone of the finished UI\n    const real = $('.qc-bp-ui--real', pl);\n    if (!real || $('.qc-bp-ui--wire', pl)) return;\n    const wire = real.cloneNode(true);\n    wire.classList.remove('qc-bp-ui--real'); wire.classList.add('qc-bp-ui--wire');\n    real.parentNode.insertBefore(wire, real);\n  });\n  \/\/ Per-frame values go straight onto the leaf nodes that use them (not as inherited custom properties on\n  \/\/ .qc-bp-iso or the plates), so a scrub frame restyles a handful of elements instead of whole plate subtrees.\n  const guides = $$('.qc-bp-guide');\n  const bars = parts.map((li) => $('.qc-bp-part-bar', li));\n  const nodes = plates.map((pl) => ({ real: $('.qc-bp-ui--real', pl), sides: $$('.qc-bp-side', pl), scan: $('.qc-bp-scan', pl), ticks: $('.qc-bp-ticks', pl) }));\n  const coEls = cos.map((co) => ({ lead: $('.lead', co), dot: $('.dot', co), bl: $('.bl', co) }));\n\n  const geo = { w: 420, h: 280, t: 12, g: 116, cx: 0, cy: 0, sw: 1, sh: 1, dx: 58, dy: -30 };\n  const PHI = (60 * Math.PI) \/ 180, C = Math.SQRT1_2;\n  function measure() {\n    const cs = getComputedStyle(scene2);\n    geo.w = iso.offsetWidth; geo.h = iso.offsetHeight;\n    geo.t = parseFloat(cs.getPropertyValue('--t')) || 12;\n    geo.g = parseFloat(cs.getPropertyValue('--g')) || 116;\n    geo.sw = scene2.clientWidth; geo.sh = scene2.clientHeight;\n    geo.cx = iso.offsetLeft + geo.w \/ 2; geo.cy = iso.offsetTop + geo.h \/ 2;\n    overlay.setAttribute('viewBox', `0 0 ${geo.sw} ${geo.sh}`);\n    const ph = phone();                                    \/\/ balloon offsets, read here and not per frame\n    geo.dx = ph ? 24 : window.innerWidth <= 1100 ? 44 : 58; geo.dy = ph ? -16 : -30;\n  }\n  const project = (x, y, z) => [geo.cx + C * (x + y), geo.cy + C * (y - x) * Math.cos(PHI) - z * Math.sin(PHI)];\n\n  let lastKey = '';\n  function apply(p) {\n    const ex = ease(seg(p, 0, 0.2));\n    const zp = geo.t + ex * geo.g;                         \/\/ half the stack height\n    plates.forEach((pl, i) => { pl.style.transform = `translateZ(${((1 - i) * zp).toFixed(2)}px)`; });\n    guides.forEach((gd) => { gd.style.height = `${(2 * zp).toFixed(2)}px`; gd.style.transform = `translateZ(${(-zp).toFixed(2)}px) rotateX(90deg)`; });\n    const rs = [seg(p, 0.64, 0.86), seg(p, 0.42, 0.64), seg(p, 0.2, 0.42)];   \/\/ built from the ground up\n    const running = p >= 0.9;\n    const active = [0, 1, 2].find((i) => rs[i] > 0 && rs[i] < 1) ?? -1;\n    plates.forEach((pl, i) => {\n      const r = rs[i], n = nodes[i];\n      n.real.style.clipPath = `inset(0 ${((1 - r) * 100).toFixed(2)}% 0 0)`;\n      n.sides.forEach((sd) => sd.style.setProperty('--r', r.toFixed(4)));   \/\/ leaves: only their ::before uses it\n      n.scan.style.transform = `translateX(${(r * geo.w).toFixed(1)}px)`;\n      n.ticks.style.setProperty('--r', r.toFixed(4));    \/\/ a leaf; keeps the CSS \"hidden until .is-in\" rule in charge\n      pl.classList.toggle('is-scan', r > 0 && r < 1);\n    });\n    bars.forEach((b, i) => { b.style.transform = `scaleX(${rs[i].toFixed(4)})`; });\n\n    const key = `${active}|${rs.map((r) => (r >= 1 ? 1 : 0)).join('')}|${running}`;\n    if (key !== lastKey) {\n      lastKey = key;\n      parts.forEach((li, i) => { li.classList.toggle('is-active', i === active); li.classList.toggle('is-done', rs[i] >= 1); });\n      cos.forEach((co, i) => { co.classList.toggle('is-active', i === active); co.classList.toggle('is-done', rs[i] >= 1); });\n      build.classList.toggle('is-running', running);\n      if (running) setStatus(buildStatus, 'running', 'Running');\n      else if (active >= 0) setStatus(buildStatus, 'rendering', `Rendering ${['website', 'app', 'system'][active]}`);\n      else if (rs[0] >= 1) setStatus(buildStatus, 'building', 'Connecting');\n      else if (rs[2] >= 1) setStatus(buildStatus, 'rendering', 'Rendering');\n      else setStatus(buildStatus, 'drafting', p > 0.01 ? 'Expanding' : 'Wireframe');\n    }\n\n    \/\/ balloons with leader lines, attached to each plate's right corner\n    const { dx, dy } = geo, op = clamp(ex * 1.6 - 0.3).toFixed(3);\n    cos.forEach((co, i) => {\n      const [ax, ay] = project(geo.w \/ 2, geo.h \/ 2, (1 - i) * zp);\n      const bx = ax + dx, by = ay + dy, el = coEls[i];\n      co.style.opacity = op;\n      el.lead.setAttribute('d', `M${ax.toFixed(1)} ${ay.toFixed(1)} L${bx.toFixed(1)} ${by.toFixed(1)}`);\n      el.dot.setAttribute('cx', ax.toFixed(1)); el.dot.setAttribute('cy', ay.toFixed(1));\n      el.bl.setAttribute('transform', `translate(${bx.toFixed(1)} ${by.toFixed(1)})`);\n    });\n  }\n\n  let scrollOn = false;\n  measure();\n  apply(STILL ? 1 : 0);\n\n  function setupStatic() {\n    build.classList.add('is-static', 'is-in');\n    hero.classList.add('is-static');                     \/\/ hides the \"Scroll to build\" cue\n    measure(); apply(1);\n  }\n\n  \/\/ Does the tallest state of the copy (the longest part description open) fit the pinned frame?\n  \/\/ If not (short or zoomed screens, user text spacing), section 2 goes static, so nothing is clipped: the link included.\n  const copy2 = $('.qc-bp-copy2');\n  const stage2 = $('.qc-bp-stage2');\n  function copyFits() {\n    const inner = parts.map((li) => $('.qc-bp-part-dw > div', li));\n    const shown = inner.reduce((s, d) => s + d.clientHeight, 0);\n    const full = inner.map((d) => d.scrollHeight);\n    const need = copy2.offsetHeight - shown + (compact() ? Math.max(...full) : full.reduce((s, h) => s + h, 0));\n    return (stacked() ? stage2.offsetHeight : 0) + need <= pin.clientHeight;\n  }\n\n  function setupScroll() {\n    const { gsap, ScrollTrigger } = window;\n    gsap.registerPlugin(ScrollTrigger);\n    scrollOn = true;\n    const proxy = { p: 0 };\n    const onRefreshInit = () => { if (!dead) { syncHeader(); layoutHero(); } };\n    ScrollTrigger.addEventListener('refreshInit', onRefreshInit);\n    undo.push(() => ScrollTrigger.removeEventListener('refreshInit', onRefreshInit));\n    let fitTimer = 0, ro = null;\n    const toStatic = () => {\n      if (!scrollOn || dead) return;\n      scrollOn = false;\n      if (ro) ro.disconnect();\n      ctx.revert();\n      heroExit = 0; stage.style.removeProperty('--exit'); if (mark3d) mark3d.wake();\n      setupStatic();\n    };\n    const checkFit = () => { if (scrollOn && alive() && !copyFits()) toStatic(); };\n    const ctx = gsap.context(() => {\n      gsap.to(proxy, {\n        p: 1, ease: 'none',\n        onUpdate: () => apply(proxy.p),\n        scrollTrigger: {\n          trigger: pin,\n          start: () => 'top top+=' + pinTop(),                        \/\/ pins right under the sticky header\n          end: () => '+=' + Math.round((window.innerHeight - pinTop()) * (stacked() ? 1.7 : 2.2)),\n          pin: true, scrub: 0.6, anticipatePin: 1, invalidateOnRefresh: true,\n          onRefresh: () => { measure(); apply(proxy.p); clearTimeout(fitTimer); fitTimer = setTimeout(checkFit, 0); },   \/\/ fonts, zoom, resize\n        },\n      });\n      ScrollTrigger.create({ trigger: build, start: 'top 85%', once: true, onEnter: () => build.classList.add('is-in') });\n      ScrollTrigger.create({\n        trigger: hero, start: () => 'top top+=' + pinTop(), end: () => 'bottom top+=' + pinTop(),\n        onUpdate: (s) => { heroExit = s.progress; stage.style.setProperty('--exit', s.progress.toFixed(3)); if (mark3d) mark3d.wake(); },\n      });\n    });\n    undo.push(() => { ctx.revert(); clearTimeout(fitTimer); if (ro) ro.disconnect(); });\n    \/\/ text spacing or font changes applied after load don't resize the window: watch the copy itself\n    if ('ResizeObserver' in window) {\n      ro = new ResizeObserver(() => { clearTimeout(fitTimer); fitTimer = setTimeout(checkFit, 250); });\n      ro.observe(copy2);\n    }\n    ScrollTrigger.refresh();\n    if (document.fonts && document.fonts.ready) document.fonts.ready.then(() => { if (!dead) ScrollTrigger.refresh(); });\n  }\n\n  \/\/ GSAP comes from the loader at the top of this fragment, which fires \"qcgsap\" once ScrollTrigger has run (or\n  \/\/ failed). Scripts it added while the page was loading delay the page's load event, so the load event is the\n  \/\/ other sure moment. With nothing on its way (a page without the loader), the load event decides.\n  function whenGsap(cb) {\n    if (GSAP()) return cb(true);\n    let fired = false;\n    const done = () => { if (!fired) { fired = true; cb(!!GSAP()); } };\n    on(window, 'qcgsap', done, { once: true });\n    if (document.readyState !== 'complete') on(window, 'load', done, { once: true });\n    else if (!window.__qcGsapLoading) done();\n  }\n\n  if (STILL || SHORT) setupStatic();\n  else whenGsap((ok) => { if (!alive()) return; ok && copyFits() ? setupScroll() : setupStatic(); });\n\n  const io2 = new IntersectionObserver(([en]) => build.classList.toggle('is-offscreen', !en.isIntersecting));\n  io2.observe(build);\n  undo.push(() => io2.disconnect());\n\n  \/\/ Resize: re-centre the drawing at once (cheap), then, debounced, re-read the header and recompute pins.\n  \/\/ Height-only resizes on phones (address bar showing\/hiding) skip the refresh, so nothing jumps mid-scroll.\n  let lastW = window.innerWidth, resizeTimer = 0, resizeRaf = 0;\n  on(window, 'resize', () => {\n    if (!resizeRaf) resizeRaf = requestAnimationFrame(() => { resizeRaf = 0; if (alive()) layoutHero(); });   \/\/ once per frame\n    clearTimeout(resizeTimer);\n    resizeTimer = setTimeout(() => {\n      if (!alive()) return;\n      const widthChanged = window.innerWidth !== lastW;\n      lastW = window.innerWidth;\n      const headerChanged = syncHeader();\n      layoutHero();\n      if (!scrollOn) { measure(); apply(1); }\n      else if (widthChanged || headerChanged) window.ScrollTrigger.refresh();\n    }, 150);\n  });\n  undo.push(() => { clearTimeout(resizeTimer); cancelAnimationFrame(resizeRaf); });\n  if (STILL && document.fonts && document.fonts.ready) document.fonts.ready.then(() => { if (alive()) { layoutHero(); measure(); apply(1); } });\n\n  return { root, destroy };\n}\n<\/script>\n\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-597f9b3d e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"597f9b3d\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7f2a2831 elementor-widget elementor-widget-html\" data-id=\"7f2a2831\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<style>\n.qc-showcase {\n  \/* Sticky site header. The script replaces these with the measured header bottom (admin bar included). *\/\n  --qcsc-hdr: 76px;\n  --qcsc-top: var(--qcsc-hdr);\n  \/* Elementor content width (Site Settings > Layout), so the copy lines up with the header and the hero. *\/\n  --qcsc-box: var(--container-max-width, 1140px);\n  --qcsc-gut: max(24px, calc((100% - var(--qcsc-box)) \/ 2));\n  --qcsc-cycle: 2.6s;\n  --ink: #0A0E17; --paper: #F6F5F0; --volt: #C8F53B;\n  --ink-900: #0F1420; --ink-800: #161D2C; --ink-700: #212A3B; --ink-600: #2F3A4F;\n  --ink-500: #4A5568; --ink-400: #6B7588; --ink-300: #9AA3B2; --amber: #FFB547;\n  --sans: \"Readex Pro\", \"Segoe UI\", Tahoma, sans-serif;\n  --mono: \"JetBrains Mono\", \"Readex Pro\", Consolas, monospace;\n  --out: cubic-bezier(.2, .8, .2, 1);\n  position: relative;\n  width: 100%;\n  background: var(--ink);\n  color: var(--paper);\n  font: 400 17px\/1.55 var(--sans);\n  text-align: left;\n  border-top: 1px solid var(--ink-700);\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n@media (max-width: 767px) { .qc-showcase { --qcsc-hdr: 64px; } }\n.qc-showcase *, .qc-showcase *::before, .qc-showcase *::after { box-sizing: border-box; }\n\/* (0,1,1) and later in the page than the theme\/kit CSS, so it beats \".elementor-kit-N p\" *\/\n.qc-showcase :is(h2, h3, p, ol, ul, li, figure) { margin: 0; padding: 0; }\n.qc-showcase :is(ol, ul) { list-style: none; }\n.qc-showcase svg { display: block; }\n.qc-showcase :focus-visible { outline: 2px solid var(--volt); outline-offset: 4px; border-radius: 4px; }\n.qc-showcase .qcsc-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n.qc-showcase .qcsc-img { display: block; width: 100%; height: auto; max-width: none; border: 0; border-radius: 0; box-shadow: none; }\n\n\/* ---------- shared bits ---------- *\/\n.qc-showcase .qcsc-label { font: 500 14px\/1.4 var(--mono); color: var(--volt); letter-spacing: .02em; text-transform: none; }\n.qc-showcase .qcsc-idx { font: 500 13px\/1.4 var(--mono); color: var(--ink-300); letter-spacing: .02em; }\n.qc-showcase .qcsc-cat { font: 500 13px\/1.4 var(--mono); color: var(--volt); letter-spacing: .02em; white-space: nowrap; }\n.qc-showcase a.qcsc-go, .qc-showcase .qcsc-go {\n  display: inline-flex; align-items: center; gap: 8px; padding: 0 0 5px;\n  font: 600 16px\/1.2 var(--sans); color: var(--paper); text-decoration: none;\n  border-bottom: 1px solid var(--ink-500); background: none; transition: border-color .2s;\n}\n.qc-showcase .qcsc-go svg { flex: none; color: var(--volt); fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: transform .25s var(--out); }\n.qc-showcase a.qcsc-go:is(:hover, :focus-visible, :active), .qc-showcase .qcsc-gcard:hover .qcsc-go { color: var(--paper); border-bottom-color: var(--volt); text-decoration: none; }\n.qc-showcase a.qcsc-go:hover svg, .qc-showcase .qcsc-gcard:hover .qcsc-go svg { transform: translateX(3px); }\n\n\/* ---------- section head ---------- *\/\n.qc-showcase .qcsc-head {\n  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 22px 48px;\n  padding: 112px var(--qcsc-gut) 52px;\n}\n\/* same heading scale as the hero's \"What we build\" right above *\/\n.qc-showcase .qcsc-h2 {\n  margin-top: 16px; max-width: 15ch;\n  font: 700 clamp(34px, 3.6vw, 52px)\/1.04 var(--sans); letter-spacing: -.035em; color: var(--paper); text-transform: none;\n}\n@media (max-width: 760px) {\n  .qc-showcase .qcsc-h2 { margin-top: 8px; font-size: 30px; }\n  .qc-showcase .qcsc-label { font-size: 13px; }\n}\n.qc-showcase .qcsc-aside { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 32px; padding-bottom: 6px; }\n.qc-showcase .qcsc-hint { display: none; align-items: center; gap: 10px; font: 500 13px\/1.4 var(--mono); color: var(--ink-300); }\n.qc-showcase .qcsc-hint svg { color: var(--volt); fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }\n.qc-showcase.qcsc--js .qcsc-hint { display: inline-flex; }\n.qc-showcase[data-qcsc-mode=\"track\"] .qcsc-hint svg { transform: rotate(-90deg); }\n\n\/* ---------- static grid: no JS, reduced motion, Elementor editor ---------- *\/\n.qc-showcase .qcsc-grid {\n  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 48px 24px;\n  padding: 0 var(--qcsc-gut) 120px;\n}\n.qc-showcase.qcsc--js .qcsc-grid { display: none; }\n.qc-showcase .qcsc-gcard { position: relative; display: flex; flex-direction: column; align-items: flex-start; }\n.qc-showcase .qcsc-gcard-media { width: 100%; aspect-ratio: 16 \/ 10; overflow: hidden; border-radius: 16px; border: 1px solid var(--ink-700); background: var(--ink-800); }\n.qc-showcase .qcsc-gcard-media .qcsc-img { height: 100%; object-fit: cover; }\n.qc-showcase .qcsc-gcard-meta { display: flex; gap: 14px; margin-top: 22px; }\n.qc-showcase .qcsc-gcard-meta .qcsc-idx { color: var(--ink-400); }\n.qc-showcase .qcsc-gcard-title { margin-top: 10px; font: 600 24px\/1.2 var(--sans); letter-spacing: -.02em; color: var(--paper); }\n.qc-showcase .qcsc-gcard-title a.qcsc-stretch { color: inherit; text-decoration: none; }\n.qc-showcase .qcsc-gcard-title a.qcsc-stretch::after { content: \"\"; position: absolute; inset: 0; border-radius: 16px; }\n.qc-showcase .qcsc-gcard-tagline { margin-top: 8px; font-size: 16px; line-height: 1.5; color: var(--ink-300); }\n.qc-showcase .qcsc-gcard .qcsc-go { margin-top: 18px; }\n@media (max-width: 1023px) { .qc-showcase .qcsc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }\n@media (max-width: 639px) {\n  .qc-showcase .qcsc-head { padding: 80px var(--qcsc-gut) 32px; }\n  .qc-showcase .qcsc-grid { grid-template-columns: minmax(0, 1fr); gap: 40px; padding-bottom: 88px; }\n}\n.qc-showcase:not(.qcsc--js) .qcsc-mount { display: none; }\n\n\/* ======================================================================\n   DESKTOP STAGE (>= 1024px, built by the script)\n   ====================================================================== *\/\n\/* No clip on the pinned element itself: overflow\/contain on it makes Chrome record a layout shift each time\n   ScrollTrigger switches it to and from position: fixed. The clip sits on .qcsc-pin-in, which fills it. *\/\n.qc-showcase .qcsc-pin {\n  position: relative; background: var(--ink);\n  height: calc(100vh - var(--qcsc-hdr)); height: calc(100svh - var(--qcsc-hdr));\n}\n\/* the drawing sheet, as in the hero; faded in at the top so the section head reads as one piece *\/\n.qc-showcase .qcsc-pin::before {\n  content: \"\"; position: absolute; inset: 0; pointer-events: none;\n  background-image:\n    radial-gradient(circle, rgba(246,245,240,.08) 1.2px, transparent 1.7px),\n    linear-gradient(rgba(246,245,240,.028) 1px, transparent 1px),\n    linear-gradient(90deg, rgba(246,245,240,.028) 1px, transparent 1px);\n  background-size: 44px 44px, 176px 176px, 176px 176px;\n  background-position: -22px -22px, 0 0, 0 0;\n  -webkit-mask-image: linear-gradient(transparent, #000 120px); mask-image: linear-gradient(transparent, #000 120px);\n}\n.qc-showcase .qcsc-pin-in {\n  position: relative; height: 100%; overflow: hidden;\n  display: grid; grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); column-gap: clamp(28px, 4vw, 72px);\n  padding: clamp(20px, 3.4vh, 40px) max(24px, calc(var(--qcsc-gut) - 200px)) clamp(20px, 3.4vh, 40px) var(--qcsc-gut);\n}\n\n\/* left column: the active project + the rail *\/\n.qc-showcase .qcsc-side { display: flex; flex-direction: column; min-height: 0; }\n.qc-showcase .qcsc-texts { flex: 1 1 auto; display: grid; align-content: center; min-height: 0; padding: 12px 0 28px; }\n.qc-showcase .qcsc-text { grid-area: 1 \/ 1; visibility: hidden; }\n.qc-showcase .qcsc-text:is(.is-on, .is-out) { visibility: visible; }\n.qc-showcase .qcsc-text.is-out { pointer-events: none; }\n.qc-showcase .qcsc-meta { display: flex; align-items: baseline; gap: 16px; }\n\/* typed label: the clip and the text move in opposite directions, so the text stays put and appears letter by letter *\/\n.qc-showcase .qcsc-type { position: relative; display: inline-block; vertical-align: bottom; }\n.qc-showcase .qcsc-type-clip { display: block; overflow: hidden; }\n.qc-showcase .qcsc-type-in { display: block; white-space: nowrap; }\n.qc-showcase .qcsc-type-cur { position: absolute; inset: 0; pointer-events: none; }\n.qc-showcase .qcsc-type-cur i {\n  position: absolute; left: 100%; top: .18em; bottom: .14em; width: .55em; margin-left: .2em;\n  background: var(--volt); animation: qcsc-blink 1.1s steps(1) infinite;\n}\n@keyframes qcsc-blink { 50% { opacity: 0; } }\n.qc-showcase .qcsc-ptitle {\n  margin-top: 18px; font: 600 clamp(34px, 3.3vw, 50px)\/1.05 var(--sans); letter-spacing: -.028em; color: var(--paper); text-transform: none;\n}\n.qc-showcase .qcsc-mask { display: block; overflow: hidden; padding-bottom: .12em; margin-bottom: -.12em; }\n.qc-showcase .qcsc-rise { display: block; }\n.qc-showcase .qcsc-tagline { margin-top: 16px; max-width: 30ch; font-size: 18px; line-height: 1.5; color: var(--ink-300); }\n.qc-showcase .qcsc-cta { display: flex; flex-wrap: wrap; gap: 12px 28px; margin-top: 28px; }\n\n.qc-showcase .qcsc-rail { border-top: 1px solid var(--ink-700); }\n.qc-showcase .qcsc-rail li { position: relative; }\n.qc-showcase button.qcsc-rail-btn {\n  appearance: none; -webkit-appearance: none; position: relative;\n  display: grid; grid-template-columns: 36px minmax(0, 1fr); align-items: baseline; width: 100%; min-height: 0;\n  margin: 0; padding: 10px 0 11px; border: 0; border-radius: 0; background: none; box-shadow: none;\n  font: 400 15px\/1.3 var(--sans); letter-spacing: 0; text-transform: none; text-align: left; white-space: nowrap;\n  color: var(--ink-300); cursor: pointer; transition: color .2s;\n}\n.qc-showcase button.qcsc-rail-btn:is(:hover, :focus, :active) { color: var(--paper); background: none; border: 0; box-shadow: none; }\n.qc-showcase button.qcsc-rail-btn:focus-visible { outline-offset: 2px; }\n.qc-showcase .qcsc-rail-n { font: 500 12px\/1 var(--mono); color: var(--ink-400); transition: color .2s; }\n.qc-showcase .qcsc-rail-t { overflow: hidden; text-overflow: ellipsis; }\n.qc-showcase .qcsc-rail-bar { position: absolute; left: 36px; right: 0; bottom: -1px; height: 1px; background: var(--ink-700); overflow: hidden; }\n.qc-showcase .qcsc-rail-bar i { display: block; height: 100%; background: var(--ink-500); transform: scaleX(0); transform-origin: 0 50%; }\n.qc-showcase .qcsc-rail .is-active button.qcsc-rail-btn { color: var(--paper); }\n.qc-showcase .qcsc-rail .is-active .qcsc-rail-n { color: var(--volt); }\n.qc-showcase .qcsc-rail .is-active .qcsc-rail-bar i { background: var(--volt); }\n\n\/* right column: the stage. Sizes inside it are in cqw of the stage (fallbacks first, for browsers without container units). *\/\n.qc-showcase .qcsc-stagewrap { position: relative; min-width: 0; min-height: 0; display: grid; place-items: center; container-type: size; }\n.qc-showcase .qcsc-stage {\n  position: relative; aspect-ratio: 1000 \/ 760; container-type: inline-size;\n  width: min(100%, calc((100svh - var(--qcsc-hdr) - 96px) * 1.31));\n  width: min(100cqw, 131cqh);\n}\n.qc-showcase .qcsc-world { position: absolute; inset: 0; perspective: 2400px; perspective-origin: 45% 40%; }\n.qc-showcase .qcsc-set { position: absolute; inset: 0; display: none; transform-style: preserve-3d; transform-origin: 50% 50%; }\n.qc-showcase .qcsc-set.is-on { display: block; }\n.qc-showcase .qcsc-layer { position: absolute; margin: 0; }\n.qc-showcase .qcsc-layer[data-slot=\"main\"]   { left: 3%;  top: 17%;  width: 66%; }\n.qc-showcase .qcsc-layer[data-slot=\"back\"]   { left: 41%; top: 3%;   width: 55%; }\n.qc-showcase .qcsc-layer[data-slot=\"phone\"]  { left: 71%; top: 33%;  width: 21.5%; }\n.qc-showcase .qcsc-layer[data-slot=\"phone2\"] { left: 0;   top: 47%;  width: 17%; }\n.qc-showcase .qcsc-layer[data-slot=\"phone3\"] { left: 82%; top: 1%;   width: 16%; }\n.qc-showcase .qcsc-set .qcsc-frame { opacity: 0; }\n\/* the whole stage opens the active project's page (the accessible link is \"View project\") *\/\n.qc-showcase .qcsc-stagelink { position: absolute; inset: 0; z-index: 6; }\n\n\/* device frames (stage and cards) *\/\n.qc-showcase .qcsc-frame { position: relative; }\n.qc-showcase .qcsc-screen { position: relative; overflow: hidden; background: var(--ink-800); }\n.qc-showcase .qcsc-frame--desktop .qcsc-screen {\n  border-radius: 1.3% \/ 2.1%;\n  box-shadow: 0 0 0 1px rgba(246,245,240,.10), 0 28px 60px -18px rgba(0,0,0,.75);\n}\n.qc-showcase .qcsc-chrome { position: relative; display: block; height: 0; padding-top: 3.3%; background: var(--ink-900); }\n.qc-showcase .qcsc-chrome i { position: absolute; top: 50%; width: .9%; aspect-ratio: 1; border-radius: 50%; background: var(--ink-600); transform: translateY(-50%); }\n.qc-showcase .qcsc-chrome i:nth-child(1) { left: 1.6%; }\n.qc-showcase .qcsc-chrome i:nth-child(2) { left: 3.1%; }\n.qc-showcase .qcsc-chrome i:nth-child(3) { left: 4.6%; }\n.qc-showcase .qcsc-frame--mobile .qcsc-screen {\n  padding: 2.6%; border-radius: 15% \/ 7%; background: #05070C;\n  box-shadow: 0 0 0 1px var(--ink-600), 0 28px 56px -16px rgba(0,0,0,.8);\n}\n.qc-showcase .qcsc-frame--mobile .qcsc-shot { border-radius: 12.6% \/ 5.8%; }\n.qc-showcase .qcsc-island { position: absolute; z-index: 2; top: 2.1%; left: 50%; width: 27%; height: 2.6%; border-radius: 99px; background: #05070C; transform: translateX(-50%); }\n.qc-showcase .qcsc-shot { position: relative; display: block; overflow: hidden; background: #E9EAEE; }\n\/* stage: a screen that is still loading when the scan line passes keeps looking like the wireframe *\/\n.qc-showcase .qcsc-set .qcsc-shot {\n  background-color: var(--ink-800);\n  background-image: linear-gradient(rgba(246,245,240,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(246,245,240,.05) 1px, transparent 1px);\n  background-size: 8.333% 12.5%; background-position: -1px -1px;\n}\n.qc-showcase .qcsc-set .qcsc-frame--mobile .qcsc-shot { background-size: 25% 11.1%; }\n\n\/* stage only: the wireframe the screen is \"rendered\" from *\/\n.qc-showcase .qcsc-tag {\n  position: absolute; left: -1px; bottom: 100%; margin-bottom: 8px; margin-bottom: max(6px, .8cqw); padding: .45em .65em;\n  border-radius: 5px; background: rgba(10,14,23,.92); box-shadow: 0 0 0 1px var(--ink-700);\n  font: 500 11px\/1 var(--mono); font-size: clamp(10px, 1.05cqw, 12px); color: var(--ink-300); white-space: nowrap; opacity: 0;\n}\n.qc-showcase .qcsc-tag b { font-weight: 500; color: var(--ink-500); }\n.qc-showcase .qcsc-veil {\n  position: absolute; inset: 0; z-index: 3; overflow: hidden; background: var(--ink-800);\n  transform: translateX(101%);\n}\n.qc-showcase .qcsc-veil::before {\n  content: \"\"; position: absolute; z-index: 2; left: 0; top: 0; bottom: 0; width: 2px;\n  background: var(--volt); box-shadow: 0 0 16px 2px rgba(200,245,59,.45);\n}\n.qc-showcase .qcsc-veil-in {\n  position: absolute; inset: 0;\n  background-image: linear-gradient(rgba(246,245,240,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(246,245,240,.05) 1px, transparent 1px);\n  background-size: 8.333% 12.5%; background-position: -1px -1px;\n}\n.qc-showcase .qcsc-frame--mobile .qcsc-veil-in { background-size: 25% 11.1%; }\n.qc-showcase .qcsc-x { position: absolute; inset: 0; width: 100%; height: 100%; }\n.qc-showcase .qcsc-x path { fill: none; stroke: var(--ink-600); stroke-width: 1; vector-effect: non-scaling-stroke; }\n.qc-showcase .qcsc-veil-t {\n  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);\n  padding: .5em .9em; background: var(--ink-800); text-align: center; white-space: nowrap;\n  font: 500 12px\/1.45 var(--mono); font-size: clamp(9px, 1.2cqw, 14px); color: var(--paper);\n}\n.qc-showcase .qcsc-veil-t small { display: block; font-size: .82em; color: var(--ink-300); }\n.qc-showcase .qcsc-frame--mobile .qcsc-veil-t { font-size: clamp(8px, .95cqw, 12px); padding: .5em .4em; }\n.qc-showcase .qcsc-edge { position: absolute; z-index: 4; overflow: hidden; pointer-events: none; }\n.qc-showcase .qcsc-edge i { display: block; width: 100%; height: 100%; }\n.qc-showcase :is(.qcsc-edge--t, .qcsc-edge--b) { left: -14px; right: -14px; height: 1px; }\n.qc-showcase :is(.qcsc-edge--l, .qcsc-edge--r) { top: -14px; bottom: -14px; width: 1px; }\n.qc-showcase .qcsc-edge--t { top: -1px; } .qc-showcase .qcsc-edge--b { bottom: -1px; }\n.qc-showcase .qcsc-edge--l { left: -1px; } .qc-showcase .qcsc-edge--r { right: -1px; }\n.qc-showcase :is(.qcsc-edge--t, .qcsc-edge--b) i { background: repeating-linear-gradient(90deg, var(--volt) 0 6px, transparent 6px 10px); transform: translateX(-101%); }\n.qc-showcase :is(.qcsc-edge--l, .qcsc-edge--r) i { background: repeating-linear-gradient(180deg, var(--volt) 0 6px, transparent 6px 10px); transform: translateY(-101%); }\n\n\/* stage log: \"$ render \/work\/<slug>\/\" + build status *\/\n.qc-showcase .qcsc-log {\n  position: absolute; z-index: 5; left: 0; top: 0; pointer-events: none;\n  font: 500 12px\/1.75 var(--mono); font-size: clamp(11px, 1.15cqw, 13px); color: var(--ink-300); white-space: nowrap;\n}\n.qc-showcase .qcsc-log > span { display: block; }\n.qc-showcase .qcsc-log-cmd b { font-weight: 500; color: var(--volt); }\n.qc-showcase .qcsc-log-cmd .qcsc-type { vertical-align: top; }\n.qc-showcase .qcsc-log-cmd .qcsc-type-cur i { width: .5em; top: .3em; bottom: .3em; }\n.qc-showcase .qcsc-log-path { color: var(--paper); }\n.qc-showcase .qcsc-log-st i { display: inline-block; width: .55em; height: .55em; margin-right: .6em; border-radius: 50%; background: var(--amber); vertical-align: .05em; }\n.qc-showcase .qcsc-log[data-st=\"ready\"] .qcsc-log-st i { background: var(--volt); }\n\n\/* laptops and short screens *\/\n@media (max-height: 800px) {\n  .qc-showcase .qcsc-ptitle { font-size: clamp(30px, 2.9vw, 42px); margin-top: 14px; }\n  .qc-showcase .qcsc-tagline { font-size: 16.5px; margin-top: 12px; }\n  .qc-showcase .qcsc-cta { margin-top: 20px; }\n  .qc-showcase button.qcsc-rail-btn { padding: 7px 0 8px; font-size: 14px; }\n  .qc-showcase .qcsc-texts { padding-bottom: 18px; }\n}\n@media (max-width: 1180px) {\n  .qc-showcase .qcsc-pin-in { grid-template-columns: minmax(260px, 330px) minmax(0, 1fr); }\n}\n\n\/* ======================================================================\n   TRACK: phones and tablets (< 1024px), built by the script\n   ====================================================================== *\/\n.qc-showcase .qcsc-trackwrap { padding-bottom: 96px; }\n.qc-showcase .qcsc-track {\n  position: relative; display: flex; gap: 14px; margin: 0; padding: 4px var(--qcsc-gut) 6px;\n  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;\n  scroll-snap-type: x mandatory; scroll-padding-inline: var(--qcsc-gut);\n  scrollbar-width: none; -webkit-overflow-scrolling: touch;\n}\n.qc-showcase .qcsc-track::-webkit-scrollbar { display: none; }\n\/* tablets: the next card peeks in and fades out, so the cut reads as intended (never on the last card) *\/\n@media (min-width: 640px) {\n  .qc-showcase .qcsc-track {\n    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 96px), transparent);\n    mask-image: linear-gradient(90deg, #000 calc(100% - 96px), transparent);\n  }\n  .qc-showcase .qcsc-track.is-end { -webkit-mask-image: none; mask-image: none; }\n}\n.qc-showcase .qcsc-card { position: relative; flex: 0 0 min(84vw, 460px); scroll-snap-align: start; scroll-snap-stop: always; }\n.qc-showcase .qcsc-media {\n  position: relative; aspect-ratio: 4 \/ 3.2; overflow: hidden; isolation: isolate;\n  border-radius: 16px; border: 1px solid var(--ink-700);\n  background-color: var(--ink-900);\n  background-image:\n    radial-gradient(circle, rgba(246,245,240,.09) 1.1px, transparent 1.6px),\n    linear-gradient(rgba(246,245,240,.03) 1px, transparent 1px),\n    linear-gradient(90deg, rgba(246,245,240,.03) 1px, transparent 1px);\n  background-size: 24px 24px, 96px 96px, 96px 96px;\n  background-position: -12px -12px, -1px -1px, -1px -1px;\n}\n.qc-showcase .qcsc-slide { position: absolute; inset: 0; opacity: 0; transform: translate3d(7%, 0, 0); transition: opacity .55s ease, transform .9s var(--out); }\n.qc-showcase .qcsc-slide.is-on { opacity: 1; transform: none; }\n.qc-showcase .qcsc-slide.is-off { opacity: 0; transform: translate3d(-7%, 0, 0); }\n.qc-showcase .qcsc-slide .qcsc-frame { position: absolute; }\n.qc-showcase .qcsc-slide .qcsc-frame--desktop { left: 7%; top: 16%; width: 112%; }\n.qc-showcase .qcsc-slide .qcsc-frame--mobile { left: 50%; top: 16%; width: 43%; transform: translateX(-50%); }\n\/* two phones: the cycle's screen in front, another one of the project's phone screens behind it to the right *\/\n.qc-showcase .qcsc-slide--pair .qcsc-frame--mobile { top: 12%; width: 40%; left: 17%; transform: none; }\n.qc-showcase .qcsc-slide--pair .qcsc-frame--mobile:first-child { top: 22%; width: 36%; left: 51%; }\n.qc-showcase .qcsc-slide .qcsc-frame--desktop .qcsc-screen { box-shadow: 0 0 0 1px rgba(246,245,240,.10), 0 18px 36px -12px rgba(0,0,0,.7); }\n.qc-showcase .qcsc-slide .qcsc-frame--mobile .qcsc-screen { box-shadow: 0 0 0 1px var(--ink-600), 0 18px 36px -12px rgba(0,0,0,.7); }\n.qc-showcase .qcsc-bars { position: absolute; z-index: 3; left: 14px; top: 14px; display: flex; gap: 5px; }\n.qc-showcase .qcsc-bar { display: block; width: 24px; height: 3px; overflow: hidden; border-radius: 3px; background: rgba(246,245,240,.2); }\n.qc-showcase .qcsc-bar b { display: block; height: 100%; background: var(--paper); transform: scaleX(0); transform-origin: 0 50%; }\n.qc-showcase .qcsc-bar.is-done b { transform: scaleX(1); }\n.qc-showcase .qcsc-card.is-live .qcsc-bar.is-on b { animation: qcsc-fill var(--qcsc-cycle) linear forwards; animation-play-state: paused; }\n.qc-showcase .qcsc-trackwrap.is-running .qcsc-card.is-live .qcsc-bar.is-on b { animation-play-state: running; }\n@keyframes qcsc-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }\n\/* the screen name sits in the empty strip at the top, level with the progress bars, never on a screen *\/\n.qc-showcase .qcsc-mtag {\n  position: absolute; z-index: 3; right: 12px; top: 9px; padding: 0;\n  font: 500 11px\/1 var(--mono); color: var(--ink-300); white-space: nowrap;\n}\n.qc-showcase .qcsc-mtag b { font-weight: 500; color: var(--ink-500); }\n.qc-showcase .qcsc-card-body { padding: 20px 2px 0; }\n.qc-showcase .qcsc-card-meta { display: flex; align-items: baseline; gap: 14px; }\n.qc-showcase .qcsc-card-meta .qcsc-idx { color: var(--ink-400); font-size: 12.5px; }\n.qc-showcase .qcsc-card-meta .qcsc-cat { font-size: 12.5px; }\n.qc-showcase .qcsc-card-title { margin-top: 10px; font: 600 24px\/1.15 var(--sans); letter-spacing: -.02em; color: var(--paper); text-transform: none; }\n.qc-showcase .qcsc-card-tagline { margin-top: 8px; font-size: 16px; line-height: 1.5; color: var(--ink-300); }\n.qc-showcase .qcsc-card .qcsc-go { margin-top: 18px; }\n.qc-showcase .qcsc-card a.qcsc-go::after { content: \"\"; position: absolute; inset: 0; border-radius: 16px; }\n.qc-showcase .qcsc-ctrl { display: flex; align-items: center; gap: 10px; padding: 26px var(--qcsc-gut) 0; }\n.qc-showcase button.qcsc-cbtn {\n  appearance: none; -webkit-appearance: none; display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n  min-width: 44px; height: 44px; margin: 0; padding: 0 12px; border: 1px solid var(--ink-600); border-radius: 12px;\n  background: transparent; box-shadow: none; color: var(--paper); cursor: pointer;\n  font: 500 12.5px\/1 var(--mono); letter-spacing: .01em; text-transform: none; white-space: nowrap; transition: border-color .2s, color .2s;\n}\n.qc-showcase button.qcsc-cbtn:is(:hover, :focus, :active) { border-color: var(--volt); background: transparent; color: var(--paper); box-shadow: none; }\n.qc-showcase button.qcsc-cbtn:disabled { opacity: .35; cursor: default; border-color: var(--ink-600); }\n.qc-showcase button.qcsc-cbtn svg { flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }\n.qc-showcase button.qcsc-cbtn .qcsc-ico-play { display: none; fill: currentColor; stroke: none; }\n.qc-showcase button.qcsc-cbtn[data-paused] .qcsc-ico-play { display: block; }\n.qc-showcase button.qcsc-cbtn[data-paused] .qcsc-ico-pause { display: none; }\n.qc-showcase .qcsc-ico-pause { fill: currentColor; stroke: none; }\n.qc-showcase .qcsc-count { margin-left: 8px; font: 500 13px\/1 var(--mono); color: var(--ink-300); white-space: nowrap; }\n.qc-showcase .qcsc-count b { font-weight: 500; color: var(--paper); }\n.qc-showcase button.qcsc-pause { margin-left: auto; width: 44px; padding: 0; color: var(--ink-300); }\n@media (max-width: 639px) {\n  .qc-showcase .qcsc-trackwrap { padding-bottom: 80px; }\n  .qc-showcase .qcsc-ctrl { gap: 8px; }\n  .qc-showcase .qcsc-card-title { font-size: 22px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .qc-showcase *, .qc-showcase *::before, .qc-showcase *::after { animation: none !important; transition: none !important; }\n}\n@media (forced-colors: active) {\n  .qc-showcase .qcsc-veil, .qc-showcase .qcsc-edge { display: none; }\n  .qc-showcase .qcsc-set .qcsc-frame, .qc-showcase .qcsc-tag { opacity: 1 !important; }\n  .qc-showcase .qcsc-track { -webkit-mask-image: none; mask-image: none; }\n}\n<\/style>\n\n<section class=\"qc-showcase\" data-qcsc aria-labelledby=\"qcsc-title\">\n<script data-cfasync=\"false\">\n\/* Runs while the page is parsing: switch to the motion layout early (the static grid stays hidden), unless the\n   visitor prefers reduced motion or this is the Elementor editor. Safety net: if the main script never starts,\n   the static grid comes back 4 s after load. *\/\n(function (d) {\n  var s = d.currentScript, r = s && s.parentNode;\n  if (!r || !r.classList) return;\n  var editor = \/[?&]elementor-preview=\/.test(location.search) || (d.body && d.body.classList.contains('elementor-editor-active'));\n  var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;\n  if (editor || reduce) return;\n  r.classList.add('qcsc--js');\n  window.addEventListener('load', function () { setTimeout(function () { if (!r.hasAttribute('data-qcsc-ready')) r.classList.remove('qcsc--js'); }, 4000); });\n})(document);\n<\/script>\n  <div class=\"qcsc-head\">\n    <div>\n      <p class=\"qcsc-label\">\/\/ Selected work<\/p>\n      <h2 class=\"qcsc-h2\" id=\"qcsc-title\">Software built to run a real business.<\/h2>\n    <\/div>\n    <div class=\"qcsc-aside\">\n      <p class=\"qcsc-hint\" aria-hidden=\"true\"><svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\"><path d=\"M8 2.5v11M3.5 9 8 13.5 12.5 9\"\/><\/svg><span class=\"qcsc-hint-t\">Scroll to see each project<\/span><\/p>\n      <a class=\"qcsc-go\" href=\"\/work\/\">See all work<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/a>\n    <\/div>\n  <\/div>\n\n    <ul class=\"qcsc-grid\">\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover.webp\" alt=\"Sales CRM screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">01<\/span> <span class=\"qcsc-cat\">\/\/ Systems<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/sales-crm\/\">Sales CRM<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">A sales CRM with WhatsApp built in, so every lead, call and chat is in one place.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover.webp\" alt=\"Inventory and sales system screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">02<\/span> <span class=\"qcsc-cat\">\/\/ Systems<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/inventory-sales\/\">Inventory &amp; Sales System<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">An inventory management system for every branch, so stock, sales and purchasing live in one place.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover.webp\" alt=\"Booking system screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">03<\/span> <span class=\"qcsc-cat\">\/\/ Systems<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/booking-system\/\">Booking &amp; Scheduling System<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">A booking system for clinics and salons, including online booking, a staff calendar and reminders.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover.webp\" alt=\"Fashion online store screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">04<\/span> <span class=\"qcsc-cat\">\/\/ Stores<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/online-store\/\">Fashion Online Store<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">A fashion ecommerce website with a short checkout, as well as one admin to ship orders.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover.webp\" alt=\"AI document assistant screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">05<\/span> <span class=\"qcsc-cat\">\/\/ AI<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/ai-document-assistant\/\">AI Document Assistant<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">An AI document assistant for your company files. Ask a question, then get the answer with its source.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n    <li class=\"qcsc-gcard\">\n      <div class=\"qcsc-gcard-media\"><img loading=\"lazy\" class=\"qcsc-img\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover.webp\" alt=\"Delivery app and dispatch board screens\" width=\"1600\" height=\"1000\" loading=\"lazy\" decoding=\"async\"><\/div>\n      <p class=\"qcsc-gcard-meta\"><span class=\"qcsc-idx\">06<\/span> <span class=\"qcsc-cat\">\/\/ Apps<\/span><\/p>\n      <h3 class=\"qcsc-gcard-title\"><a class=\"qcsc-stretch\" href=\"\/work\/delivery-app\/\">Delivery App and Dispatch Board<\/a><\/h3>\n      <p class=\"qcsc-gcard-tagline\">A delivery driver app for delivery teams, together with a live dispatch map.<\/p>\n      <span class=\"qcsc-go\" aria-hidden=\"true\">View project<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/span>\n    <\/li>\n  <\/ul>\n\n  <div class=\"qcsc-mount\"><\/div>\n\n  <script type=\"application\/json\" class=\"qc-showcase-data\">\n[\n{\"slug\":\"sales-crm\",\"title\":\"Sales CRM\",\"category\":\"Systems\",\"tagline\":\"A sales CRM with WhatsApp built in, so every lead, call and chat is in one place.\",\"url\":\"\/work\/sales-crm\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp\",\"alt\":\"Sales CRM deals pipeline: one column per stage with totals and next steps. Quiet deals are marked, and a deal can be dragged to a new stage or closed as won or lost.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"Pipeline\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-contact.webp\",\"alt\":\"Sales CRM contact profile: details, the open deal and its next step, the full activity timeline, and the WhatsApp conversation side by side.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"Contact\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-mobile-today.webp\",\"alt\":\"Sales CRM on mobile: each rep's follow-ups for today, with one tap to call or open WhatsApp.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"Today\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-mobile-log-call.webp\",\"alt\":\"Mobile: after a call, the rep picks the outcome, adds a note and schedules the next follow-up.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"LogCall\"}]},\n{\"slug\":\"inventory-sales\",\"title\":\"Inventory & Sales System\",\"category\":\"Systems\",\"tagline\":\"An inventory management system for every branch, so stock, sales and purchasing live in one place.\",\"url\":\"\/work\/inventory-sales\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-stock-overview.webp\",\"alt\":\"Inventory system stock overview: every store and the warehouse in one table, as well as low-stock alerts and bulk transfers.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"StockOverview\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-pos-checkout.webp\",\"alt\":\"Point of sale: barcode scanning, member discounts and live stock for this store.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"PosCheckout\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-mobile-stock-lookup.webp\",\"alt\":\"Inventory system on mobile: scan a barcode to see stock at every location.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"StockLookup\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-mobile-transfer-request.webp\",\"alt\":\"Transfer request: ask the warehouse for stock in a few taps.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"TransferRequest\"}]},\n{\"slug\":\"booking-system\",\"title\":\"Booking & Scheduling System\",\"category\":\"Systems\",\"tagline\":\"A booking system for clinics and salons, including online booking, a staff calendar and reminders.\",\"url\":\"\/work\/booking-system\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-01-staff-calendar.webp\",\"alt\":\"Booking system staff calendar: bookings by service, breaks, a live time line, and the booking panel with deposit, reminders and visit history.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"StaffCalendar\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-02-booking-page.webp\",\"alt\":\"Booking system public page: clients choose a service, a therapist and a free time, then pay a deposit to hold it.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"BookingPage\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-04-mobile-confirmation.webp\",\"alt\":\"Booking system confirmation on the client's phone, with the deposit receipt, reminder plan and add-to-calendar.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"Confirmation\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-05-mobile-upcoming-visit.webp\",\"alt\":\"Upcoming visit: time, staff, directions and what to prepare, with one-tap reschedule or cancel.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"UpcomingVisit\"}]},\n{\"slug\":\"online-store\",\"title\":\"Fashion Online Store\",\"category\":\"Stores\",\"tagline\":\"A fashion ecommerce website with a short checkout, as well as one admin to ship orders.\",\"url\":\"\/work\/online-store\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-storefront-home.webp\",\"alt\":\"Home page with a campaign photo you can shop from, and quick links to each category.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"StorefrontHome\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-storefront-listing.webp\",\"alt\":\"Fashion ecommerce website collection page with filters for size, color, material and price. Quick add also opens the bag at the side.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"StorefrontListing\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-mobile-product.webp\",\"alt\":\"Mobile product page: large swipeable photos, color and size pickers that show stock on each size, and an Add to bag bar that stays on screen.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"Product\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-mobile-checkout-delivery.webp\",\"alt\":\"Fashion ecommerce website, mobile checkout: bag summary, delivery address and delivery options with clear prices.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"CheckoutDelivery\"}]},\n{\"slug\":\"ai-document-assistant\",\"title\":\"AI Document Assistant\",\"category\":\"AI\",\"tagline\":\"An AI document assistant for your company files. Ask a question, then get the answer with its source.\",\"url\":\"\/work\/ai-document-assistant\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-chat.webp\",\"alt\":\"Ask: every answer shows its sources, and the passage it used is highlighted on the page.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"Chat\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-library.webp\",\"alt\":\"AI document assistant library: see which files are ready, still processing, or need attention.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"Library\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-mobile-ask.webp\",\"alt\":\"AI document assistant on mobile: ask a quick question by typing or by voice.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"Ask\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-mobile-answer.webp\",\"alt\":\"Mobile answer: step-by-step instructions with the manual pages they came from.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"Answer\"}]},\n{\"slug\":\"delivery-app\",\"title\":\"Delivery App and Dispatch Board\",\"category\":\"Apps\",\"tagline\":\"A delivery driver app for delivery teams, together with a live dispatch map.\",\"url\":\"\/work\/delivery-app\/\",\"cover\":{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-cover.webp\",\"alt\":\"\",\"w\":1600,\"h\":1000},\"images\":[{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-dispatcher-dashboard.webp\",\"alt\":\"Dispatch board: every driver and route on a live map, unassigned orders with a suggested driver, and order statuses at the top.\",\"kind\":\"desktop\",\"w\":1440,\"h\":900,\"label\":\"DispatcherDashboard\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-route-list.webp\",\"alt\":\"Delivery driver app: today's route with progress, the next stop and the cash still to collect.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"RouteList\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-order-detail.webp\",\"alt\":\"Order detail: route to the stop, customer details, cash to collect and a one-tap handoff to the phone's maps app.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"OrderDetail\"},{\"src\":\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-proof-of-delivery.webp\",\"alt\":\"Delivery driver app, proof of delivery: parcel scan, photo at the door, recipient and signature, then cash with change.\",\"kind\":\"mobile\",\"w\":390,\"h\":844,\"label\":\"ProofOfDelivery\"}]}\n]\n<\/script>\n<\/section>\n\n<script data-cfasync=\"false\">\n\/* Qcoders showcase. Data-driven: everything below is built from the JSON block above. *\/\n(() => {\n  'use strict';\n  const GSAP_SRC = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.12.5\/gsap.min.js';\n  const ST_SRC = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.12.5\/ScrollTrigger.min.js';\n  const mq = (q) => window.matchMedia(q);\n  const WIDE = mq('(min-width: 1024px)');\n  const SHOWN = mq('(min-width: 768px)');   \/\/ the section is hidden below this (the phone layout)\n  const REDUCE = mq('(prefers-reduced-motion: reduce)');\n  const FINE = mq('(hover: hover) and (pointer: fine)');\n  const EDITOR = () => \/[?&]elementor-preview=\/.test(location.search) || document.body.classList.contains('elementor-editor-active') ||\n    !!(window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode());\n  const MIN_PIN_H = 520;                 \/\/ shorter than this (e.g. a 1280 x 600 window) -> the card layout\n  const SEG = 0.72;                      \/\/ scroll per project, in pinned-frame heights\n  const pad = (n) => String(n).padStart(2, '0');\n  const clamp = (v, a, b) => Math.min(b, Math.max(a, v));\n  const esc = (s) => String(s == null ? '' : s).replace(\/[&<>\"']\/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n  const CHEV = '<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg>';\n\n  \/* ---------------------------------------------------------- GSAP: reuse, or load once *\/\n  \/\/ The hero's loader adds GSAP + ScrollTrigger from 768px up (and fires \"qcgsap\" once they have run): wait for it.\n  \/\/ Without the hero, tags printed on the page are waited for until the page has loaded; only when no tag is on the\n  \/\/ page (or it failed) are the same pinned cdnjs files added, once per page.\n  function loadGsap() {\n    if (window.__qcscGsap) return window.__qcscGsap;\n    const has = () => !!(window.gsap && window.ScrollTrigger);\n    window.__qcscGsap = new Promise((resolve) => {\n      if (has()) return resolve(true);\n      const tagged = () => [...document.scripts].some((s) => \/\\\/(gsap|ScrollTrigger)(\\.min)?\\.js(\\?|$)\/i.test(s.src));\n      const inject = () => {\n        if (has()) return resolve(true);\n        const add = (src) => new Promise((res) => {\n          const s = document.createElement('script');\n          s.src = src; s.async = false; s.setAttribute('data-cfasync', 'false');\n          s.onload = () => res(); s.onerror = () => res();\n          document.head.appendChild(s);\n        });\n        Promise.all([window.gsap ? 0 : add(GSAP_SRC), window.ScrollTrigger ? 0 : add(ST_SRC)]).then(() => resolve(has()));\n        setTimeout(() => resolve(has()), 10000);\n      };\n      const atLoad = () => (document.readyState === 'complete' ? inject() : window.addEventListener('load', inject, { once: true }));\n      \/\/ the hero's loader is fetching them (it fires \"qcgsap\" when ScrollTrigger has run): wait for it, add nothing\n      const wait = () => { window.addEventListener('qcgsap', () => resolve(has()), { once: true }); setTimeout(() => resolve(has()), 10000); };\n      const check = () => (has() ? resolve(true) : window.__qcGsapLoading ? wait() : tagged() ? atLoad() : inject());\n      if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', check, { once: true });\n      else check();\n    });\n    return window.__qcscGsap;\n  }\n\n  \/* ---------------------------------------------------------- sticky site header (same rules as the hero) *\/\n  function readHeader(root) {\n    const hdr = document.querySelector('.elementor-location-header');\n    if (!hdr || hdr.contains(root)) return null;\n    const stuck = hdr.querySelector('.elementor-sticky--active');\n    const r = (stuck || hdr).getBoundingClientRect();\n    if (r.height < 1) return null;\n    const bar = document.getElementById('wpadminbar');\n    const barH = bar && getComputedStyle(bar).position === 'fixed' ? Math.round(bar.getBoundingClientRect().height) : 0;\n    const sticky = !!stuck || \/sticky|fixed\/.test(getComputedStyle(hdr).position) || !!hdr.querySelector('.elementor-sticky, [data-settings*=\"sticky\"]');\n    const bottom = Math.round(stuck ? r.bottom : barH + r.height);\n    return { fit: bottom, pin: sticky ? bottom : barH };\n  }\n\n  \/* ---------------------------------------------------------- markup *\/\n  const SLOT = {                         \/\/ depth (px) and scroll parallax (px each way) per layer\n    back: { z: -260, k: 16 }, phone3: { z: -150, k: 24 }, main: { z: 0, k: 34 }, phone2: { z: 110, k: 54 }, phone: { z: 190, k: 72 },\n  };\n  const BUILD_ORDER = ['back', 'phone3', 'main', 'phone2', 'phone'];\n  function slotsFor(images) {\n    const d = images.filter((m) => m.kind !== 'mobile'), m = images.filter((m) => m.kind === 'mobile');\n    const out = [];\n    if (d[0]) out.push(['main', d[0]]);\n    if (d[1]) out.push(['back', d[1]]); else if (m[2]) out.push(['phone3', m[2]]);\n    if (m[0]) out.push(['phone', m[0]]);\n    if (m[1]) out.push(['phone2', m[1]]);\n    return out;\n  }\n  function cycleFor(images) {\n    const d = images.filter((m) => m.kind !== 'mobile'), m = images.filter((m) => m.kind === 'mobile');\n    return [d[0], m[0], d[1] || m[1], d[2], m[2]].filter(Boolean).slice(0, 3);\n  }\n  \/\/ a phone slide shows a second phone screen behind it (one the cycle does not show), like the covers\n  const partnerFor = (images, cyc, im) => im.kind === 'mobile' ? images.find((x) => x.kind === 'mobile' && x !== im && !cyc.includes(x)) : null;\n  const tag = (label) => `<b>&lt;<\/b>${esc(label)}<b> \/&gt;<\/b>`;\n  \/\/ withTag: the <Label \/> pill above a rendered screen. Only the two desktop layers get one; on the phones it\n  \/\/ would sit on top of another screen (their wireframe still names them during the build).\n  function device(im, stage, withTag) {\n    const mob = im.kind === 'mobile';\n    const w = +im.w || (mob ? 390 : 1440), h = +im.h || (mob ? 844 : 900);\n    return `<div class=\"qcsc-frame qcsc-frame--${mob ? 'mobile' : 'desktop'}\">` +\n      (withTag ? `<span class=\"qcsc-tag\" aria-hidden=\"true\">${tag(im.label)}<\/span>` : '') +\n      '<div class=\"qcsc-screen\">' +\n        (mob ? '<span class=\"qcsc-island\" aria-hidden=\"true\"><\/span>' : '<span class=\"qcsc-chrome\" aria-hidden=\"true\"><i><\/i><i><\/i><i><\/i><\/span>') +\n        `<span class=\"qcsc-shot\" style=\"aspect-ratio:${w}\/${h}\"><img class=\"qcsc-img\" data-src=\"${esc(im.src)}\" alt=\"${esc(im.alt)}\" width=\"${w}\" height=\"${h}\" loading=\"lazy\" decoding=\"async\"><\/span>` +\n        (stage ? '<span class=\"qcsc-veil\" aria-hidden=\"true\"><span class=\"qcsc-veil-in\"><svg class=\"qcsc-x\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\"><path d=\"M0 0 100 100M100 0 0 100\"\/><\/svg>' +\n          `<span class=\"qcsc-veil-t\">&lt;${esc(im.label)} \/&gt;<small>${w} &times; ${h}<\/small><\/span><\/span><\/span>` : '') +\n      '<\/div>' +\n      (stage ? '<span class=\"qcsc-edge qcsc-edge--t\"><i><\/i><\/span><span class=\"qcsc-edge qcsc-edge--r\"><i><\/i><\/span><span class=\"qcsc-edge qcsc-edge--b\"><i><\/i><\/span><span class=\"qcsc-edge qcsc-edge--l\"><i><\/i><\/span>' : '') +\n    '<\/div>';\n  }\n  const typed = (cls, text) => `<span class=\"qcsc-type\"><span class=\"qcsc-type-clip\"><span class=\"qcsc-type-in ${cls}\">${text}<\/span><\/span><span class=\"qcsc-type-cur\"><i><\/i><\/span><\/span>`;\n\n  function stageHTML(data, allUrl) {\n    const N = data.length;\n    \/\/ the last project also offers \"See all work\" (the head's link has scrolled away by then), on the same line\n    const texts = data.map((p, i) => `\n      <div class=\"qcsc-text${i ? '' : ' is-on'}\" data-i=\"${i}\">\n        <p class=\"qcsc-meta\" data-a><span class=\"qcsc-idx\">${pad(i + 1)} \/ ${pad(N)}<\/span>${typed('qcsc-cat', '\/\/ ' + esc(p.category))}<\/p>\n        <h3 class=\"qcsc-ptitle\"><span class=\"qcsc-mask\"><span class=\"qcsc-rise\">${esc(p.title)}<\/span><\/span><\/h3>\n        <p class=\"qcsc-tagline\" data-a>${esc(p.tagline)}<\/p>\n        <p class=\"qcsc-cta\" data-a><a class=\"qcsc-go\" href=\"${esc(p.url)}\">View project<span class=\"qcsc-sr\">: ${esc(p.title)}<\/span>${CHEV}<\/a>` +\n          (i === N - 1 && allUrl ? `<a class=\"qcsc-go qcsc-all\" href=\"${esc(allUrl)}\">See all work${CHEV}<\/a>` : '') + `<\/p>\n      <\/div>`).join('');\n    const rail = data.map((p, i) => `<li${i ? '' : ' class=\"is-active\"'}><button type=\"button\" class=\"qcsc-rail-btn\" data-i=\"${i}\"${i ? '' : ' aria-current=\"true\"'}>` +\n      `<span class=\"qcsc-rail-n\" aria-hidden=\"true\">${pad(i + 1)}<\/span><span class=\"qcsc-rail-t\">${esc(p.title)}<\/span><span class=\"qcsc-rail-bar\" aria-hidden=\"true\"><i><\/i><\/span><\/button><\/li>`).join('');\n    const sets = data.map((p, i) => `<div class=\"qcsc-set\" data-i=\"${i}\" aria-hidden=\"true\">` +\n      slotsFor(p.images || []).map(([slot, im]) => `<div class=\"qcsc-layer\" data-slot=\"${slot}\">${device(im, true, slot === 'main' || slot === 'back')}<\/div>`).join('') + '<\/div>').join('');\n    return `\n  <div class=\"qcsc-pin\">\n    <div class=\"qcsc-pin-in\">\n      <div class=\"qcsc-side\">\n        <div class=\"qcsc-texts\">${texts}\n        <\/div>\n        <div class=\"qcsc-railwrap\" role=\"group\" aria-label=\"Projects\">\n          <ol class=\"qcsc-rail\">${rail}<\/ol>\n        <\/div>\n      <\/div>\n      <div class=\"qcsc-stagewrap\">\n        <div class=\"qcsc-stage\">\n          <p class=\"qcsc-log\" aria-hidden=\"true\" data-st=\"drafting\">\n            <span class=\"qcsc-log-cmd\"><b>$<\/b> render ${typed('qcsc-log-path', esc(data[0].url))}<\/span>\n            <span class=\"qcsc-log-st\"><i><\/i><span class=\"qcsc-log-t\">Drafting<\/span> <span class=\"qcsc-log-n\"><\/span><\/span>\n          <\/p>\n          <div class=\"qcsc-world\">${sets}<\/div>\n          <a class=\"qcsc-stagelink\" href=\"${esc(data[0].url)}\" tabindex=\"-1\" aria-hidden=\"true\"><\/a>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>`;\n  }\n\n  function trackHTML(data) {\n    const N = data.length;\n    const cards = data.map((p, i) => {\n      const cyc = cycleFor(p.images || []);\n      return `\n      <li class=\"qcsc-card\" data-i=\"${i}\">\n        <div class=\"qcsc-media\" aria-live=\"off\">\n          ${cyc.length > 1 ? `<span class=\"qcsc-bars\" aria-hidden=\"true\">${cyc.map((_, k) => `<span class=\"qcsc-bar${k ? '' : ' is-on'}\"><b><\/b><\/span>`).join('')}<\/span>` : ''}\n          ${cyc.map((im, k) => { const two = partnerFor(p.images || [], cyc, im); return `<div class=\"qcsc-slide qcsc-slide--${im.kind === 'mobile' ? 'mobile' : 'desktop'}${two ? ' qcsc-slide--pair' : ''}${k ? '' : ' is-on'}\"${k ? ' aria-hidden=\"true\"' : ''} data-label=\"${esc(im.label)}\">${two ? device(two, false) : ''}${device(im, false)}<\/div>`; }).join('')}\n          <span class=\"qcsc-mtag\" aria-hidden=\"true\">${tag(cyc[0] ? cyc[0].label : '')}<\/span>\n        <\/div>\n        <div class=\"qcsc-card-body\">\n          <p class=\"qcsc-card-meta\"><span class=\"qcsc-idx\">${pad(i + 1)}<\/span><span class=\"qcsc-cat\">\/\/ ${esc(p.category)}<\/span><\/p>\n          <h3 class=\"qcsc-card-title\">${esc(p.title)}<\/h3>\n          <p class=\"qcsc-card-tagline\">${esc(p.tagline)}<\/p>\n          <a class=\"qcsc-go\" href=\"${esc(p.url)}\">View project<span class=\"qcsc-sr\">: ${esc(p.title)}<\/span>${CHEV}<\/a>\n        <\/div>\n      <\/li>`;\n    }).join('');\n    return `\n  <div class=\"qcsc-trackwrap\">\n    <ul class=\"qcsc-track\" aria-label=\"Projects\">${cards}\n    <\/ul>\n    <div class=\"qcsc-ctrl\">\n      <button type=\"button\" class=\"qcsc-cbtn qcsc-prev\" aria-label=\"Previous project\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M10 3.5 5.5 8l4.5 4.5\"\/><\/svg><\/button>\n      <button type=\"button\" class=\"qcsc-cbtn qcsc-next\" aria-label=\"Next project\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M6 3.5 10.5 8 6 12.5\"\/><\/svg><\/button>\n      <span class=\"qcsc-count\" aria-hidden=\"true\"><b>01<\/b> \/ ${pad(N)}<\/span>\n      <button type=\"button\" class=\"qcsc-cbtn qcsc-pause\" aria-label=\"Pause screens\"><svg class=\"qcsc-ico-pause\" viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M4 3h3v10H4zM9 3h3v10H9z\"\/><\/svg><svg class=\"qcsc-ico-play\" viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" aria-hidden=\"true\"><path d=\"M4.5 2.8 13 8l-8.5 5.2z\"\/><\/svg><\/button>\n    <\/div>\n  <\/div>`;\n  }\n\n  const setSrc = (img) => { if (img && !img.getAttribute('src') && img.dataset.src) img.src = img.dataset.src; };\n  const warm = (url) => { if (url) { const im = new Image(); im.decoding = 'async'; im.src = url; } };\n\n  \/* ======================================================================\n     STAGE: desktop, pinned and scroll-driven\n     Scroll moves the camera (tilt + parallax) continuously. Crossing into another project plays a fixed-length,\n     choreographed build: frame outlines plot, the wireframe shows the component name, then a scan line wipes the\n     real screen in, back to front. Fast scrolling speeds the running build up instead of stacking builds.\n     ====================================================================== *\/\n  function Stage(root, mount, data, onUnfit) {\n    const N = data.length;\n    const allLink = root.querySelector('.qcsc-head a.qcsc-go');\n    mount.innerHTML = stageHTML(data, (allLink && allLink.getAttribute('href')) || '\/work\/');\n    const $ = (s, el = mount) => el.querySelector(s);\n    const $$ = (s, el = mount) => [...el.querySelectorAll(s)];\n    const pin = $('.qcsc-pin'), side = $('.qcsc-side'), stage = $('.qcsc-stage'), wrap = $('.qcsc-stagewrap');\n    const stageLink = $('.qcsc-stagelink');\n    const texts = $$('.qcsc-text'), lis = $$('.qcsc-rail li'), btns = $$('.qcsc-rail-btn'), fills = $$('.qcsc-rail-bar i');\n    const log = $('.qcsc-log'), logPath = $('.qcsc-log-path'), logT = $('.qcsc-log-t'), logN = $('.qcsc-log-n');\n    const sets = $$('.qcsc-set').map((el) => {\n      const layers = BUILD_ORDER.map((n) => $(`[data-slot=\"${n}\"]`, el)).filter(Boolean).map((layer) => ({\n        layer, slot: SLOT[layer.dataset.slot], frame: $('.qcsc-frame', layer), tag: $('.qcsc-tag', layer),\n        veil: $('.qcsc-veil', layer), veilIn: $('.qcsc-veil-in', layer), veilT: $('.qcsc-veil-t', layer), edges: $$('.qcsc-edge', layer),\n        eT: $('.qcsc-edge--t i', layer), eR: $('.qcsc-edge--r i', layer), eB: $('.qcsc-edge--b i', layer), eL: $('.qcsc-edge--l i', layer),\n        img: $('img', layer),\n      }));\n      return { el, layers, imgs: layers.map((L) => L.img), ok: false, waiting: null };\n    });\n    const undo = [];\n    const on = (t, type, fn, opts) => { t.addEventListener(type, fn, opts); undo.push(() => t.removeEventListener(type, fn, opts)); };\n    let dead = false, started = false, entered = false;\n    let cur = -1, want = 0, busy = false, tl = null, tlTo = -1, tail = null;\n    let jumpTo = -1, jumpT = 0;            \/\/ a rail click: go straight there, ignore the projects the smooth scroll passes\n    let p = 0, mainST = null, ctx = null;\n    const shown = new Set();\n    const ptr = { x: 0, y: 0 };\n    const fillVal = new Array(N).fill(-1);\n\n    let hdrKey = '';\n    function syncHeader() {\n      const h = readHeader(root);\n      const key = h ? `${h.fit}\/${h.pin}` : 'css';\n      if (key === hdrKey) return;\n      hdrKey = key;\n      if (h) { root.style.setProperty('--qcsc-hdr', h.fit + 'px'); root.style.setProperty('--qcsc-top', h.pin + 'px'); }\n      else { root.style.removeProperty('--qcsc-hdr'); root.style.removeProperty('--qcsc-top'); }\n    }\n    const pinTop = () => parseFloat(getComputedStyle(root).getPropertyValue('--qcsc-top')) || 0;\n    syncHeader();\n\n    \/* images: load the set being built, warm the next one in scroll direction *\/\n    function load(i) { const s = sets[i]; if (s) s.imgs.forEach((img) => { img.loading = 'eager'; setSrc(img); }); }\n    function preload(i) { const s = sets[i]; if (s && !s.warm) { s.warm = true; s.imgs.forEach((img) => warm(img.dataset.src)); } }\n    function ready(i) {\n      const s = sets[i];\n      if (s.ok) return Promise.resolve();\n      if (!s.waiting) {\n        const one = (img) => (img.decode ? img.decode() : new Promise((r) => { if (img.complete) r(); else { img.onload = r; img.onerror = r; } })).catch(() => {});\n        s.waiting = Promise.race([Promise.all(s.imgs.map(one)), new Promise((r) => setTimeout(r, 2500))]).then(() => { s.ok = true; });\n      }\n      return s.waiting;\n    }\n\n    \/* scroll-linked camera: tilt follows the scroll through each project, layers parallax by depth *\/\n    function place(i, t) {\n      const s = sets[i];\n      const u = clamp(t - i - 0.5, -0.9, 0.9);\n      const ry = -11 + u * 9 + ptr.x * 3, rx = 6 - u * 4 - ptr.y * 2.5;\n      s.el.style.transform = `rotateX(${rx.toFixed(2)}deg) rotateY(${ry.toFixed(2)}deg)`;\n      for (const L of s.layers) L.layer.style.transform = `translate3d(0, ${(-u * 2 * L.slot.k).toFixed(1)}px, ${L.slot.z}px)`;\n    }\n    function render() {\n      if (dead) return;\n      const t = p * N;\n      if (!entered && p > 0.0005) enter();\n      if (entered) {\n        const idx = clamp(Math.floor(t), 0, N - 1);\n        if (jumpTo >= 0) { if (idx === jumpTo) jumpTo = -1; }\n        \/\/ hysteresis: switching needs 0.1 project past the boundary, so small scroll adjustments don't rebuild\n        else if (idx > want ? t >= idx + 0.1 : idx < want ? t < idx + 0.9 : false) request(idx);\n      }\n      for (let i = 0; i < N; i++) {\n        const f = Math.round(clamp(t - i, 0, 1) * 1000) \/ 1000;\n        if (f !== fillVal[i]) { fillVal[i] = f; fills[i].style.transform = `scaleX(${f})`; }\n      }\n      shown.forEach((i) => place(i, t));\n    }\n    \/\/ the scrub and the two pointer tweens all update in the same GSAP tick: render once, right after it\n    let queued = false;\n    const flush = () => { queued = false; render(); };\n    const later = typeof queueMicrotask === 'function' ? (f) => queueMicrotask(f) : (f) => Promise.resolve().then(f);\n    const schedule = () => { if (!queued) { queued = true; later(flush); } };\n    function setRail(i) {\n      lis.forEach((li, k) => li.classList.toggle('is-active', k === i));\n      btns.forEach((b, k) => (k === i ? b.setAttribute('aria-current', 'true') : b.removeAttribute('aria-current')));\n    }\n    function showSet(i) { shown.add(i); sets[i].el.classList.add('is-on'); place(i, p * N); }\n    function hideSet(i) { if (i === cur) return; shown.delete(i); sets[i].el.classList.remove('is-on'); }\n\n    \/* the \"$ render \/work\/<slug>\/\" line *\/\n    function typeIn(el, gsap, delay) {\n      const box = el.closest('.qcsc-type'), clip = $('.qcsc-type-clip', box), cur = $('.qcsc-type-cur', box);\n      const n = Math.max(1, el.textContent.length), d = Math.min(0.75, n * 0.032), ease = `steps(${n})`;\n      const T = gsap.timeline({ delay });\n      T.fromTo([clip, cur], { xPercent: -100 }, { xPercent: 0, duration: d, ease }, 0).fromTo(el, { xPercent: 100 }, { xPercent: 0, duration: d, ease }, 0);\n      return T;\n    }\n    function logState(st, done, total) {\n      log.dataset.st = st;\n      logT.textContent = st === 'ready' ? 'Ready' : st === 'rendering' ? 'Rendering' : 'Drafting';\n      logN.textContent = `${done}\/${total} screens`;\n    }\n\n    function buildSet(i, dir) {\n      const { gsap } = window;\n      const s = sets[i], n = s.layers.length;\n      const B = gsap.timeline();\n      B.add(() => { logPath.textContent = data[i].url; logState('drafting', 0, n); }, 0);\n      B.add(typeIn(logPath, gsap, 0), 0);\n      s.layers.forEach((L, k) => {\n        const o = k * 0.09;\n        \/\/ x\/y: 0 clears the px offset GSAP reads from the CSS resting state (translate(\u00b1101%))\n        B.set(L.edges, { opacity: 1 }, 0).set([L.veil, L.veilIn], { x: 0, xPercent: 0 }, 0);\n        B.fromTo(L.frame, { opacity: 0, y: 34 * dir, scale: 0.985 }, { opacity: 1, y: 0, scale: 1, duration: 0.6, ease: 'power3.out' }, o);\n        \/\/ the outline plots clockwise: top, right, bottom, left\n        B.fromTo(L.eT, { x: 0, xPercent: -101 }, { xPercent: 0, duration: 0.16, ease: 'none' }, o + 0.04)\n          .fromTo(L.eR, { y: 0, yPercent: -101 }, { yPercent: 0, duration: 0.14, ease: 'none' }, o + 0.2)\n          .fromTo(L.eB, { x: 0, xPercent: 101 }, { xPercent: 0, duration: 0.16, ease: 'none' }, o + 0.34)\n          .fromTo(L.eL, { y: 0, yPercent: 101 }, { yPercent: 0, duration: 0.14, ease: 'none' }, o + 0.5);\n        B.fromTo(L.veilT, { opacity: 0 }, { opacity: 1, duration: 0.3 }, o + 0.12);\n        if (L.tag) B.set(L.tag, { opacity: 0 }, 0);\n      });\n      s.layers.forEach((L, k) => {\n        const w = Stage.WIPE + k * 0.1;\n        B.to(L.veil, { xPercent: 101, duration: 0.66, ease: 'power3.inOut' }, w)\n          .to(L.veilIn, { xPercent: -101, duration: 0.66, ease: 'power3.inOut' }, w)\n          .to(L.edges, { opacity: Stage.TRACE, duration: 0.5 }, w + 0.5);\n        if (L.tag) B.fromTo(L.tag, { opacity: 0, x: -8 }, { opacity: 1, x: 0, duration: 0.45, ease: 'power2.out' }, w + 0.5);\n        B.add(() => logState(k + 1 === n ? 'ready' : 'rendering', k + 1, n), w + 0.62);\n      });\n      return B;\n    }\n    const readyAt = (n) => Stage.WIPE + (n - 1) * 0.1 + 0.62;     \/\/ when buildSet's log says \"Ready\"\n    function exitSet(i, dir) {\n      const { gsap } = window;\n      const X = gsap.timeline();\n      \/\/ short, so the old light screens are gone before the new wireframes come in (no muddy overlap)\n      sets[i].layers.slice().reverse().forEach((L, k) => {\n        X.to(L.frame, { opacity: 0, y: -46 * dir, scale: 0.97, duration: 0.28, ease: 'power2.in' }, k * 0.03);\n      });\n      return X;\n    }\n    function textOut(i, dir) {\n      const { gsap } = window;\n      const el = texts[i];\n      el.classList.add('is-out');\n      \/\/ hide it when done, unless textIn took it back meanwhile (that removes is-out)\n      return gsap.timeline({ onComplete: () => { if (el.classList.contains('is-out')) el.classList.remove('is-on', 'is-out'); } })\n        .to(el.querySelectorAll('[data-a], .qcsc-rise'), { opacity: 0, y: -14 * dir, duration: 0.24, ease: 'power2.in', stagger: 0.03 });\n    }\n    const caretOff = (el) => window.gsap.to($('.qcsc-type-cur', el), { opacity: 0, duration: 0.2 });\n    function textIn(i, dir) {\n      const { gsap } = window;\n      const el = texts[i];\n      texts.forEach((t, k) => { if (k !== i && !t.classList.contains('is-out')) t.classList.remove('is-on'); });\n      el.classList.remove('is-out'); el.classList.add('is-on');\n      const parts = el.querySelectorAll('[data-a]');\n      const T = gsap.timeline();\n      T.set(parts, { opacity: 1, y: 0 }, 0)\n        .set($('.qcsc-type-cur', el), { opacity: 1 }, 0)\n        .add(typeIn($('.qcsc-type-in', el), gsap, 0), 0.04)\n        .add(caretOff(el), 0.9)                                   \/\/ a terminal has one caret: the log keeps it\n        .fromTo($('.qcsc-rise', el), { yPercent: 108 * dir, y: 0, opacity: 1 }, { yPercent: 0, duration: 0.7, ease: 'power3.out' }, 0.06)\n        .fromTo(parts[1], { opacity: 0, y: 12 * dir }, { opacity: 1, y: 0, duration: 0.5, ease: 'power2.out' }, 0.16)\n        .fromTo(parts[2], { opacity: 0, y: 10 * dir }, { opacity: 1, y: 0, duration: 0.45, ease: 'power2.out' }, 0.26);\n      return T;\n    }\n\n    \/\/ the rail marks the project the stage actually shows (set when it switches, in step), never a pending one\n    function request(i) {\n      want = i;\n      if (busy) {\n        \/\/ speed the running build up; a build that is holding for its images resumes on its own once they decode\n        if (tl && tlTo !== i) tl.timeScale(2.6);\n        return;\n      }\n      step();\n    }\n    function step() {\n      if (dead || busy || want === cur) return;\n      const { gsap } = window;\n      const from = cur, to = want, dir = from < 0 || to > from ? 1 : -1;\n      busy = true; tlTo = to;\n      load(to); preload(to + dir);\n      \/\/ released when the log says \"Ready\": the cosmetic tail (outlines fading, tags) keeps playing on its own\n      const release = () => { if (tl !== T) return; busy = false; tl = null; tlTo = -1; step(); };\n      const T = gsap.timeline({ onComplete: release });\n      tl = tail = T;\n      let at = 0;\n      if (from >= 0) {\n        T.add(exitSet(from, dir), 0).add(textOut(from, dir), 0).add(() => hideSet(from), 0.4);\n        at = 0.36;\n      }\n      T.add(() => { cur = to; showSet(to); setRail(to); stageLink.href = data[to].url; }, at);\n      T.add(buildSet(to, dir), at);\n      if (from >= 0) T.add(textIn(to, dir), at + 0.02);\n      else { texts.forEach((t, k) => t.classList.toggle('is-on', k === to)); T.add(caretOff(texts[to]), at + 0.9); }\n      T.add(release, at + readyAt(sets[to].layers.length) + 0.01);\n      \/\/ hold the wipe until this project's screens have loaded (2.5 s at most), so the scan line never reveals a blank\n      T.add(() => { if (!sets[to].ok && tl === T) { T.pause(); ready(to).then(() => { if (tl === T && !dead) T.resume(); }); } }, at + Stage.WIPE - 0.02);\n      ready(to);\n    }\n    function enter() {\n      if (entered || dead) return;\n      entered = true;\n      load(0); preload(1);\n      request(jumpTo >= 0 ? jumpTo : clamp(Math.floor(p * N), 0, N - 1));\n    }\n\n    \/\/ The copy is centred in .qcsc-texts, so overflow would spill upward where scrollHeight cannot see it: compare the\n    \/\/ tallest project text with the room it has instead (offsetHeight ignores the animation transforms).\n    const textsBox = $('.qcsc-texts');\n    function fits() {\n      const cs = getComputedStyle(textsBox);\n      const room = textsBox.clientHeight - parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom);\n      const need = Math.max(...texts.map((t) => t.offsetHeight));\n      return need <= room + 2 && side.scrollHeight <= side.clientHeight + 2 && stage.clientWidth >= 440;\n    }\n\n    function start() {\n      if (started || dead) return;\n      started = true;\n      const { gsap, ScrollTrigger } = window;\n      const proxy = { p: 0 };\n      const onRefreshInit = () => { if (!dead) syncHeader(); };\n      ScrollTrigger.addEventListener('refreshInit', onRefreshInit);\n      undo.push(() => ScrollTrigger.removeEventListener('refreshInit', onRefreshInit));\n      ctx = gsap.context(() => {\n        const tw = gsap.to(proxy, {\n          p: 1, ease: 'none',\n          onUpdate: () => { p = proxy.p; schedule(); },\n          scrollTrigger: {\n            trigger: pin, pin: true, scrub: 0.5, anticipatePin: 1, invalidateOnRefresh: true,\n            refreshPriority: -1,                                         \/\/ after the hero's pin, which sits above\n            start: () => 'top top+=' + pinTop(),                         \/\/ right under the sticky header\n            end: () => '+=' + Math.round(pin.offsetHeight * SEG * N),\n          },\n        });\n        mainST = tw.scrollTrigger;\n        \/\/ Both measured after the hero's pin (refreshPriority -1). The page can create these before the hero pins, so the\n        \/\/ early-load trigger acts on real scrolling only, never on a refresh measured without the hero's pin spacing.\n        ScrollTrigger.create({ trigger: pin, start: 'top 72%', end: 'max', refreshPriority: -1, onToggle: (s) => { if (s.isActive) enter(); } });\n        ScrollTrigger.create({\n          trigger: root, start: 'top bottom+=900', end: 'max', refreshPriority: -1,\n          onUpdate: (s) => { if (!ScrollTrigger.isRefreshing) { load(0); preload(1); s.kill(); } },\n        });\n      });\n      undo.push(() => ctx.revert());\n      \/\/ a soft camera that leans toward the pointer (desktop mice only)\n      if (FINE.matches) {\n        const qx = gsap.quickTo(ptr, 'x', { duration: 1, ease: 'power3', onUpdate: schedule });\n        const qy = gsap.quickTo(ptr, 'y', { duration: 1, ease: 'power3', onUpdate: schedule });\n        let rect = null;\n        on(wrap, 'pointerenter', () => { rect = wrap.getBoundingClientRect(); });\n        on(wrap, 'pointermove', (e) => {\n          if (!rect) rect = wrap.getBoundingClientRect();\n          qx(clamp(((e.clientX - rect.left) \/ rect.width) * 2 - 1, -1, 1));\n          qy(clamp(((e.clientY - rect.top) \/ rect.height) * 2 - 1, -1, 1));\n        });\n        on(wrap, 'pointerleave', () => { rect = null; qx(0); qy(0); });\n      }\n      \/\/ rail: switch to that project now; the smooth scroll then passes the others without building them.\n      \/\/ Any wheel, touch, key or pointer press hands control back to the scroll position (3 s at most).\n      const endJump = () => { if (jumpTo >= 0) { jumpTo = -1; clearTimeout(jumpT); } };\n      ['wheel', 'touchstart', 'keydown', 'pointerdown'].forEach((ev) => on(window, ev, endJump, { passive: true }));\n      undo.push(() => clearTimeout(jumpT));\n      btns.forEach((b, i) => on(b, 'click', () => {\n        if (!mainST) return;\n        jumpTo = i;\n        clearTimeout(jumpT);\n        jumpT = setTimeout(() => { jumpTo = -1; schedule(); }, 3000);\n        if (entered) request(i); else enter();\n        const y = mainST.start + (mainST.end - mainST.start) * ((i + 0.5) \/ N);\n        window.scrollTo({ top: Math.round(y), behavior: 'smooth' });\n      }));\n      \/\/ text spacing, late CSS or fonts can make the copy taller without a resize: re-check that it still fits\n      if (window.ResizeObserver) {\n        let rq = 0;\n        const ro = new ResizeObserver(() => {\n          cancelAnimationFrame(rq);\n          rq = requestAnimationFrame(() => { if (!dead && !fits()) onUnfit(); });\n        });\n        texts.forEach((el) => ro.observe(el));\n        ro.observe($('.qcsc-rail'));\n        undo.push(() => { cancelAnimationFrame(rq); ro.disconnect(); });\n      }\n      ScrollTrigger.refresh();\n      if (document.fonts && document.fonts.ready) document.fonts.ready.then(() => { if (dead) return; if (!fits()) onUnfit(); else ScrollTrigger.refresh(); });\n    }\n\n    function destroy() {\n      if (dead) return;\n      dead = true;\n      if (tl) tl.kill();\n      if (tail) tail.kill();\n      undo.splice(0).reverse().forEach((f) => { try { f(); } catch (e) { \/* already gone *\/ } });\n      mount.innerHTML = '';\n      root.style.removeProperty('--qcsc-hdr'); root.style.removeProperty('--qcsc-top');\n    }\n    return { start, destroy, fits, debug: () => ({ cur, want, busy, p, entered, st: mainST && [mainST.start, mainST.end] }) };\n  }\n  Stage.WIPE = 0.56;     \/\/ when the first scan line starts, in seconds into a build\n  Stage.TRACE = 0.16;    \/\/ what stays of the dashed outline once a screen is rendered\n\n  \/* ======================================================================\n     TRACK: phones and tablets. Swipe between projects; the card in view cycles through three of its screens.\n     The timer is the progress bar's CSS animation, so pausing it (hover, focus, touch, off screen, the Pause\n     button, a hidden tab) keeps its place for free.\n     ====================================================================== *\/\n  function Track(root, mount, data) {\n    const N = data.length;\n    mount.innerHTML = trackHTML(data);\n    const $ = (s, el = mount) => el.querySelector(s);\n    const $$ = (s, el = mount) => [...el.querySelectorAll(s)];\n    const wrap = $('.qcsc-trackwrap'), track = $('.qcsc-track'), countB = $('.qcsc-count b');\n    const prev = $('.qcsc-prev'), next = $('.qcsc-next'), pauseBtn = $('.qcsc-pause');\n    const cards = $$('.qcsc-card').map((el) => ({\n      el, i: 0, vis: false, ratio: 0, seen: false,\n      slides: $$('.qcsc-slide', el), bars: $$('.qcsc-bar', el), tag: $('.qcsc-mtag', el),\n    }));\n    const undo = [];\n    const on = (t, type, fn, opts) => { t.addEventListener(type, fn, opts); undo.push(() => t.removeEventListener(type, fn, opts)); };\n    let dead = false, inView = false, held = false, hover = false, focus = false, paused = false, current = 0, heldTimer = 0;\n\n    const loadSlide = (sl) => { if (sl) $$('img', sl).forEach(setSrc); };\n    cards.forEach((c) => loadSlide(c.slides[0]));                 \/\/ native lazy loading keeps off-screen cards waiting\n    function sync() {\n      const run = inView && !held && !hover && !focus && !paused && !document.hidden;\n      wrap.classList.toggle('is-running', run);\n      cards.forEach((c) => {\n        const live = inView && c.vis && c === cards[current];      \/\/ one card cycles at a time, even when two are in view\n        c.el.classList.toggle('is-live', live);\n        if (live && !c.seen) { c.seen = true; loadSlide(c.slides[1]); }\n      });\n    }\n    function show(c, k) {\n      const old = c.i;\n      if (k === old) return;\n      c.i = k;\n      c.slides.forEach((s, j) => {\n        s.classList.toggle('is-on', j === k);\n        s.classList.toggle('is-off', j === old);\n        if (j === k) s.removeAttribute('aria-hidden'); else s.setAttribute('aria-hidden', 'true');\n      });\n      c.bars.forEach((b, j) => { b.classList.toggle('is-done', j < k); b.classList.toggle('is-on', j === k); });\n      c.tag.innerHTML = tag(c.slides[k].dataset.label);\n      loadSlide(c.slides[k]);\n      const nx = c.slides[(k + 1) % c.slides.length];\n      loadSlide(nx);                                               \/\/ preload only the next screen\n    }\n    on(track, 'animationend', (e) => {\n      if (e.animationName !== 'qcsc-fill') return;\n      const c = cards.find((x) => x.el.contains(e.target));\n      if (c && c.slides.length > 1) show(c, (c.i + 1) % c.slides.length);\n    });\n\n    const io = new IntersectionObserver((entries) => {\n      entries.forEach((en) => { const c = cards.find((x) => x.el === en.target); c.ratio = en.intersectionRatio; c.vis = en.intersectionRatio >= 0.55; });\n      let best = 0;\n      cards.forEach((c, i) => { if (c.ratio > cards[best].ratio + 0.01) best = i; });\n      current = best;\n      countB.textContent = pad(best + 1);\n      track.classList.toggle('is-end', best === N - 1);\n      prev.disabled = best === 0; next.disabled = best === N - 1;\n      sync();\n    }, { root: track, threshold: [0, 0.25, 0.55, 0.8, 1] });\n    cards.forEach((c) => io.observe(c.el));\n    const io2 = new IntersectionObserver(([en]) => { inView = en.isIntersecting; sync(); }, { threshold: 0.15 });\n    io2.observe(track);\n    undo.push(() => { io.disconnect(); io2.disconnect(); });\n\n    \/\/ the visitor is in control while they touch, swipe, hover with a mouse or move the keyboard focus through the\n    \/\/ cards; cycling resumes 4 s after the last touch. Taps on the buttons (which also fire mouse-compatible events\n    \/\/ and focus the button on Android) do not count as hover or focus, or the cycle would stop for good.\n    const hold = () => { held = true; sync(); clearTimeout(heldTimer); heldTimer = setTimeout(() => { held = false; sync(); }, 4000); };\n    on(track, 'pointerdown', hold, { passive: true });\n    on(track, 'wheel', hold, { passive: true });\n    on(track, 'scroll', () => { if (!held) hold(); else { clearTimeout(heldTimer); heldTimer = setTimeout(() => { held = false; sync(); }, 4000); } }, { passive: true });\n    on(wrap, 'pointerenter', (e) => { if (e.pointerType === 'mouse') { hover = true; sync(); } });\n    on(wrap, 'pointerleave', (e) => { if (e.pointerType === 'mouse') { hover = false; sync(); } });\n    on(track, 'focusin', (e) => { let kb = true; try { kb = e.target.matches(':focus-visible'); } catch (_) { \/* old browser *\/ } focus = kb; sync(); });\n    on(track, 'focusout', (e) => { if (!track.contains(e.relatedTarget)) { focus = false; sync(); } });\n    on(document, 'visibilitychange', sync);\n    undo.push(() => clearTimeout(heldTimer));\n\n    function go(k) {\n      const c = cards[clamp(k, 0, N - 1)].el;\n      const padL = parseFloat(getComputedStyle(track).scrollPaddingLeft) || 0;\n      track.scrollTo({ left: c.offsetLeft - padL, behavior: REDUCE.matches ? 'auto' : 'smooth' });\n    }\n    on(prev, 'click', () => go(current - 1));\n    on(next, 'click', () => go(current + 1));\n    on(pauseBtn, 'click', () => {\n      paused = !paused;\n      pauseBtn.toggleAttribute('data-paused', paused);\n      pauseBtn.setAttribute('aria-label', paused ? 'Play screens' : 'Pause screens');\n      sync();\n    });\n    prev.disabled = true;\n\n    function destroy() {\n      if (dead) return;\n      dead = true;\n      undo.splice(0).reverse().forEach((f) => { try { f(); } catch (e) { \/* already gone *\/ } });\n      mount.innerHTML = '';\n    }\n    return { destroy, start() {}, fits: () => true };\n  }\n\n  \/* ======================================================================\n     BOOT: one instance per root; picks grid \/ stage \/ track and switches when the screen changes\n     ====================================================================== *\/\n  function boot(root) {\n    if (root.hasAttribute('data-qcsc-boot')) return;\n    root.setAttribute('data-qcsc-boot', '');\n    const mount = root.querySelector('.qcsc-mount');\n    const hintT = root.querySelector('.qcsc-hint-t');\n    let data = [];\n    try { data = JSON.parse(root.querySelector('script.qc-showcase-data').textContent); } catch (e) { data = []; }\n    data = Array.isArray(data) ? data.filter((p) => p && p.title && p.url && Array.isArray(p.images) && p.images.length) : [];\n    let view = null, mode = '', gsapFailed = false, unfit = '';\n    const sizeKey = () => `${window.innerWidth}x${window.innerHeight}`;\n\n    function choose() {\n      if (!data.length || EDITOR() || REDUCE.matches) return 'grid';\n      const h = readHeader(root);\n      const pinH = window.innerHeight - (h ? h.fit : 76);\n      if (WIDE.matches && !gsapFailed && pinH >= MIN_PIN_H && unfit !== sizeKey()) return 'stage';\n      return 'track';\n    }\n    function apply() {\n      if (!root.isConnected) { teardown(); return; }\n      \/\/ hidden (phones: the phone home shows its own tiles): build nothing and load nothing until it shows\n      if (!root.getClientRects().length) { if (view) view.destroy(); view = null; mode = ''; return; }\n      if (mode === 'stage' && view && !view.fits()) unfit = sizeKey();   \/\/ the window got shorter or the text bigger\n      const m = choose();\n      if (m === mode && (view || m === 'grid')) return;\n      \/\/ leaving the pinned stage removes its scroll length: keep a visitor who was inside it on this section\n      const r = root.getBoundingClientRect();\n      const keep = mode === 'stage' && r.top < window.innerHeight && r.bottom > 0 && r.top < 0;\n      if (view) view.destroy();\n      view = null; mode = m;\n      root.dataset.qcscMode = m;\n      root.classList.toggle('qcsc--js', m !== 'grid');\n      root.setAttribute('data-qcsc-ready', '');\n      if (m === 'grid') return;\n      hintT.textContent = m === 'stage' ? 'Scroll to see each project' : FINE.matches ? 'Use the arrows to see each project' : 'Swipe to see each project';\n      if (m === 'track') {\n        view = Track(root, mount, data);\n        if (keep) { const h = readHeader(root); window.scrollTo(0, Math.max(0, Math.round(root.getBoundingClientRect().top + window.scrollY - (h ? h.fit : 0)))); }\n        return;\n      }\n      \/\/ text spacing, zoom or a short window: if the copy does not fit the pinned frame, use the cards instead\n      const toCards = () => { if (view === v) { unfit = sizeKey(); apply(); } };\n      const v = view = Stage(root, mount, data, toCards);\n      loadGsap().then((ok) => {\n        if (view !== v) return;\n        if (!ok) { gsapFailed = true; apply(); return; }\n        if (!v.fits()) { toCards(); return; }\n        v.start();\n      });\n    }\n    let t = 0;\n    const onResize = () => { clearTimeout(t); t = setTimeout(apply, 200); };\n    const onChange = () => apply();\n    window.addEventListener('resize', onResize);\n    WIDE.addEventListener('change', onChange);\n    SHOWN.addEventListener('change', onChange);\n    REDUCE.addEventListener('change', onChange);\n    function teardown() {\n      window.removeEventListener('resize', onResize);\n      WIDE.removeEventListener('change', onChange);\n      SHOWN.removeEventListener('change', onChange);\n      REDUCE.removeEventListener('change', onChange);\n      if (view) view.destroy();\n      view = null;\n    }\n    root.__qcsc = { get view() { return view; }, get mode() { return mode; } };\n    apply();\n  }\n\n  const run = () => document.querySelectorAll('.qc-showcase[data-qcsc]').forEach(boot);\n  run();\n  \/\/ Elementor editor re-renders widgets; boot any new copy (it shows the static grid there)\n  if (window.jQuery) window.jQuery(window).on('elementor\/frontend\/init', run);\n})();\n<\/script>\n\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-70f3ac7a e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"70f3ac7a\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3dc6080f elementor-widget elementor-widget-html\" data-id=\"3dc6080f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--paper qc2-scene qch-scene\" id=\"what-we-do\">\n  <div class=\"qc2-wrap qc2-grid\">\n    <figure class=\"qc2-pic qc2-scene__pic qc2-cut\" style=\"--qc2-focus: 58% 42%; --qc2-focus-m: 58% 40%\">\n      \n      <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-ceramics-shop-owner-tablet-1200.webp\"\n           srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-ceramics-shop-owner-tablet-1200.webp 1200w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-ceramics-shop-owner-tablet.webp 2400w\"\n           sizes=\"auto, (min-width: 1024px) 60vw, 100vw\" width=\"2400\" height=\"1600\" loading=\"lazy\" decoding=\"async\"\n           alt=\"A shop owner in a linen apron taps a tablet on his counter, shelves of handmade mugs and teapots behind him\">\n    <\/figure>\n    <figure class=\"qc2-pic qc2-screen qc2-scene__screen qc2-cut qc2-cut--late\">\n      <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-pos-checkout.webp\"\n           width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\"\n           alt=\"Point of sale in our inventory system: products to tap on the left, the current sale and a card payment of $228.13 on the right\">\n    <\/figure>\n    <div class=\"qc2-scene__text qc2-stack\">\n      <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>Inventory system<\/p>\n      <h2 class=\"qc2-h\">Your business, on one screen<\/h2>\n      <p>A sale at the counter lowers the stock of that store at once, so the whole company sees it. When stock runs low, the system suggests a transfer or a purchase order.<\/p>\n      <p>That's the custom software we build for your business. Websites, online stores, apps and business systems, made around the way you already work.<\/p>\n      <div class=\"qc2-actions\"><a class=\"qc2-link\" href=\"\/work\/inventory-sales\/\">See the inventory system<\/a><\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<style data-qch=\"03\">\n\/* home page.css @03 Scene & Screen: \"Your business, on one screen\" *\/\n\/* The till lands low on the photo's inner edge, under the counter line, so the owner's hands and his\n   tablet stay in view: four columns instead of five, and it reaches the section's foot. *\/\n@container qc2 (min-width: 64rem) {\n  \/* the first light screen of the home: it fills the window under the sticky header, till included *\/\n  .qc2.qch-scene .qc2-grid { min-height: min(calc(100svh - var(--qc2-header)), 60rem); }\n  .qc2.qch-scene .qc2-scene__screen.qc2-screen { grid-column: 5 \/ span 4; margin-bottom: var(--qc2-s7); }\n}\n@container qc2 (min-width: 64rem) and (max-width: 89.99rem) {\n  .qc2.qch-scene .qc2-scene__screen.qc2-screen { grid-column: 4 \/ span 4; }\n}\n\/* The till sits at the foot of a full-height section, so on the shared range it was still half cut when\n   the section lay flush in the window. It opens while it enters instead, after the photo, and is whole\n   by the time all of it is in view. *\/\n@supports (animation-timeline: view()) {\n  @media (prefers-reduced-motion: no-preference) {\n    .qc2.qch-scene .qc2-cut--late { animation-range: entry 25% entry 100%; }\n  }\n}\n<\/style>\n<style data-qc2-guard>\n\/* make_v2.py: the margin rules above, one id stronger than Elementor's figure reset (kit-v2.css 2) *\/\n@container qc2 (min-width: 64rem) { .qc2.qch-scene .qc2-scene__screen.qc2-screen:not(#qc2-guard) { margin-bottom: var(--qc2-s7); } }\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-4bea0e53 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"4bea0e53\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2302c58c elementor-widget elementor-widget-html\" data-id=\"2302c58c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--stone qc2-ledger qc2-sec qch-ledger\" id=\"services\">\n  <div class=\"qc2-wrap\">\n    <div class=\"qc2-grid qc2-ends\">\n      <div class=\"qc2-head\">\n        <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>Services<\/p>\n        <h2 class=\"qc2-h\">Six ways we can help<\/h2>\n      <\/div>\n      <p class=\"qc2-lead qc2-muted\">Twelve services in six groups. Pick the one that sounds like your week.<\/p>\n    <\/div>\n    <ul class=\"qc2-ledger__list\">\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#web\">\n        <span class=\"qc2-ledger__name\">Websites<\/span>\n        <span class=\"qc2-ledger__line\">A website that says what you do and makes it easy to contact you.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-book\"><img loading=\"lazy\" class=\"qch-prev__desk\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-02-booking-page.webp\" width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\" alt=\"Booking system: the online booking page\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Booking system, the online booking page<\/span>\n        <\/span>\n      <\/a><\/li>\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#stores\">\n        <span class=\"qc2-ledger__name\">Online stores<\/span>\n        <span class=\"qc2-ledger__line\">Products, payments, shipping and orders in one place.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-store\"><img loading=\"lazy\" class=\"qch-prev__desk\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-online-store-r3-storefront-home.webp\" width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\" alt=\"Fashion online store: the store home page\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Online store, the home page<\/span>\n        <\/span>\n      <\/a><\/li>\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#apps\">\n        <span class=\"qc2-ledger__name\">Mobile apps<\/span>\n        <span class=\"qc2-ledger__line\">Android and iPhone apps, when your project really needs one.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-route\"><img loading=\"lazy\" class=\"qch-prev__phone\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-delivery-app-route-list.webp\" width=\"390\" height=\"844\" loading=\"lazy\" decoding=\"async\" alt=\"Delivery app: today's route on the driver's phone\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Delivery app, the driver's route for today<\/span>\n        <\/span>\n      <\/a><\/li>\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#systems\">\n        <span class=\"qc2-ledger__name\">Business systems<\/span>\n        <span class=\"qc2-ledger__line\">Customers, stock and staff in one system, including automation and dashboards.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-stock\"><img loading=\"lazy\" class=\"qch-prev__desk\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-inventory-sales-stock-overview.webp\" width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\" alt=\"Inventory system: stock overview for each branch\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Inventory &amp; sales, stock in every location<\/span>\n        <\/span>\n      <\/a><\/li>\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#ai\">\n        <span class=\"qc2-ledger__name\">AI solutions<\/span>\n        <span class=\"qc2-ledger__line\">Answers for your customers and your team, from your own files.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-docs\"><img loading=\"lazy\" class=\"qch-prev__phone\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-mobile-answer.webp\" width=\"390\" height=\"844\" loading=\"lazy\" decoding=\"async\" alt=\"AI document assistant: an answer with its source on a phone\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Document assistant, an answer with its sources<\/span>\n        <\/span>\n      <\/a><\/li>\n      <li class=\"qc2-ledger__row\"><a href=\"\/services\/#support\">\n        <span class=\"qc2-ledger__name\">Support after launch<\/span>\n        <span class=\"qc2-ledger__line\">Hosting, updates, security and honest advice before you spend.<\/span>\n        <span class=\"qc2-ledger__pic qch-prev\">\n          <span class=\"qch-prev__field qc2--f-docs\"><img loading=\"lazy\" class=\"qch-prev__desk qch-prev__desk--wide\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-ai-document-assistant-access.webp\" width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\" alt=\"AI document assistant: who can see which files\"><\/span>\n          <span class=\"qch-prev__cap\" aria-hidden=\"true\">Document assistant, who can see which files<\/span>\n        <\/span>\n      <\/a><\/li>\n    <\/ul>\n    <p class=\"qch-ledger__more\"><a class=\"qc2-link\" href=\"\/services\/\">See all 12 services<\/a><\/p>\n  <\/div>\n<\/div>\n<style data-qch=\"04\">\n\/* home page.css @04 Service ledger: \"Six ways we can help\" *\/\n\/* The preview is the product itself, standing on its own colour: a desktop screen runs off the tile's\n   right and bottom edges from its top-left corner; a phone screen stands on the tile's foot with its\n   bottom corners cut. One caption line under the tile says which product it is. *\/\n.qc2.qch-ledger .qch-prev__field { position: absolute; inset: 0; overflow: clip; }\n.qc2.qch-ledger .qch-prev__field > img { position: absolute; display: block; max-width: none; margin: 0; }\n.qc2.qch-ledger .qch-prev__desk  { left: 11%; top: 13%; width: 176%; height: auto; aspect-ratio: 16 \/ 10; }\n.qc2.qch-ledger .qch-prev__phone { left: 19%; top: 10%; width: 62%; height: auto; aspect-ratio: 390 \/ 844; border-radius: 7.2% \/ 3.33%; }\n\/* a screen whose dark sidebar would melt into its own field starts at its content instead (support:\n   the access screen from its page title, x 248 of 1440) *\/\n.qc2.qch-ledger .qch-prev__desk--wide { left: -19%; }\n.qc2.qch-ledger .qch-prev__photo { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--qc2-focus, 50% 50%); }\n.qc2.qch-ledger .qch-prev__cap { display: none; }\n.qc2.qch-ledger .qch-ledger__more { margin-top: var(--qc2-s7); }\n@container qc2 (max-width: 63.99rem) {\n  .qc2.qch-ledger .qch-prev { position: relative; overflow: clip; }\n}\n@container qc2 (min-width: 64rem) {\n  \/* the preview stands at the middle of the list, so the rows above and below it share the space beside it *\/\n  .qc2.qch-ledger .qc2-ledger__pic { top: 50%; translate: 0 -50%; }\n  .qc2.qch-ledger .qch-prev__cap {\n    display: block; position: absolute; left: 0; top: calc(100% + var(--qc2-s3));\n    font-size: var(--qc2-t-small); line-height: 1.45; color: var(--qc2-muted); font-weight: 400;\n  }\n  .qc2.qch-ledger .qch-ledger__more { margin-top: var(--qc2-s8); }\n}\n<\/style>\n<style data-qc2-guard>\n\/* make_v2.py: the margin rules above, one id stronger than Elementor's figure reset (kit-v2.css 2) *\/\n.qc2.qch-ledger .qch-prev__field > img:not(#qc2-guard) { margin: 0; }\n.qc2.qch-ledger .qch-ledger__more:not(#qc2-guard) { margin-top: var(--qc2-s7); }\n@container qc2 (min-width: 64rem) { .qc2.qch-ledger .qch-ledger__more:not(#qc2-guard) { margin-top: var(--qc2-s8); } }\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-351073c3 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"351073c3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7c62cd2b elementor-widget elementor-widget-html\" data-id=\"7c62cd2b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--paper qc2-strip qc2-sec qch-strip\" id=\"who-it-is-for\">\n  <div class=\"qc2-wrap qc2-grid qc2-ends\">\n    <div class=\"qc2-head\">\n      <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>Industries<\/p>\n      <h2 class=\"qc2-h\">Built around your daily work<\/h2>\n    <\/div>\n    <div class=\"qc2-stack\">\n      <p>For businesses of every type and size, from a two-person startup to a large company, wherever you are. Different industries share the same daily problems: orders, stock, bookings, customers and staff.<\/p>\n      <div class=\"qch-strip__bar\">\n        <a class=\"qc2-link\" href=\"\/about\/\">More about who we build for<\/a>\n        <div class=\"qch-strip__nav\" role=\"group\" aria-label=\"Scroll the kinds of business\" hidden>\n          <button type=\"button\" class=\"qch-strip__btn\" data-dir=\"-1\" aria-controls=\"who-it-is-for-list\" aria-label=\"Previous kinds of business\"><svg viewBox=\"0 0 44 76\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M38 6 L6 38 L38 70\"\/><\/svg><\/button>\n          <button type=\"button\" class=\"qch-strip__btn\" data-dir=\"1\" aria-controls=\"who-it-is-for-list\" aria-label=\"Next kinds of business\"><svg viewBox=\"0 0 44 76\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M6 6 L38 38 L6 70\"\/><\/svg><\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n  <ul class=\"qc2-strip__track\" id=\"who-it-is-for-list\" tabindex=\"0\" aria-label=\"Kinds of business we build for\">\n    <li class=\"qc2-strip__item\"><a href=\"\/work\/online-store\/\">\n      <div class=\"qc2-pic qc2-cut\" style=\"--qc2-focus: 52% 45%\">\n        \n        <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-online-seller-packing-box-1200.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-online-seller-packing-box-1200.webp 1200w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-online-seller-packing-box.webp 2400w\" sizes=\"auto, (min-width: 1680px) 20vw, (min-width: 1024px) 28vw, (min-width: 640px) 42vw, 78vw\" width=\"2400\" height=\"1600\" loading=\"lazy\" decoding=\"async\" alt=\"A young man who runs an online clothing shop packs a sweater into a kraft box at his studio table\"><\/div>\n      <h3 class=\"qc2-title\">Retail and e-commerce<\/h3>\n      <p>An online store that shows real stock and handles payments, shipping and returns in one admin.<\/p>\n      <span class=\"qc2-strip__go\">See the online store<\/span><\/a><\/li>\n    <li class=\"qc2-strip__item\"><a href=\"\/work\/delivery-app\/\">\n      <div class=\"qc2-pic qc2-cut\" style=\"--qc2-focus: 50% 28%\">\n        \n        <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-delivery-rider-next-stop-800.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-delivery-rider-next-stop-800.webp 800w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-delivery-rider-next-stop.webp 1600w\" sizes=\"auto, (min-width: 1680px) 20vw, (min-width: 1024px) 28vw, (min-width: 640px) 42vw, 78vw\" width=\"1600\" height=\"2000\" loading=\"lazy\" decoding=\"async\" alt=\"A delivery rider beside a cargo scooter loaded with parcels checks his phone on a sunny street\"><\/div>\n      <h3 class=\"qc2-title\">Logistics and delivery<\/h3>\n      <p>A driver app with routes and proof of delivery, plus a live map for the office.<\/p>\n      <span class=\"qc2-strip__go\">See the delivery app<\/span><\/a><\/li>\n    <li class=\"qc2-strip__item\"><a href=\"\/work\/booking-system\/\">\n      <div class=\"qc2-pic qc2-cut qch-tile qc2--f-book\">\n        <img loading=\"lazy\" class=\"qch-tile__desk\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-booking-system-01-staff-calendar.webp\" width=\"1440\" height=\"900\" loading=\"lazy\" decoding=\"async\" alt=\"Our booking system's team calendar: a week of bookings for each member of staff, side by side\"><\/div>\n      <h3 class=\"qc2-title\">Hospitality<\/h3>\n      <p>Table or room bookings, online orders, and one screen for stock and staff shifts.<\/p>\n      <span class=\"qc2-strip__go\">See the booking system<\/span><\/a><\/li>\n    <li class=\"qc2-strip__item\"><a href=\"\/work\/inventory-sales\/\">\n      <div class=\"qc2-pic qc2-cut\" style=\"--qc2-focus: 60% 35%\">\n        \n        <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-warehouse-aisle-stock-scan-800.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-warehouse-aisle-stock-scan-800.webp 800w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-warehouse-aisle-stock-scan.webp 1600w\" sizes=\"auto, (min-width: 1680px) 20vw, (min-width: 1024px) 28vw, (min-width: 640px) 42vw, 78vw\" width=\"1600\" height=\"2000\" loading=\"lazy\" decoding=\"async\" alt=\"A warehouse worker scans a box on a metal shelf with a handheld barcode scanner\"><\/div>\n      <h3 class=\"qc2-title\">Manufacturing and wholesale<\/h3>\n      <p>Stock across warehouses and branches, purchase orders with approval, and profit reports by product.<\/p>\n      <span class=\"qc2-strip__go\">See the inventory system<\/span><\/a><\/li>\n    <li class=\"qc2-strip__item\"><a href=\"\/work\/booking-system\/\">\n      <div class=\"qc2-pic qc2-cut\" style=\"--qc2-focus: 76% 50%\">\n        \n        <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-barbershop-front-desk-booking-1200.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-barbershop-front-desk-booking-1200.webp 1200w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-barbershop-front-desk-booking.webp 2400w\" sizes=\"auto, (min-width: 1680px) 20vw, (min-width: 1024px) 28vw, (min-width: 640px) 42vw, 78vw\" width=\"2400\" height=\"1600\" loading=\"lazy\" decoding=\"async\" alt=\"A receptionist smiles at his screen at a terrazzo front desk while a client in a long coat waits holding his phone\"><\/div>\n      <h3 class=\"qc2-title\">Clinics, salons and studios<\/h3>\n      <p>Online booking with deposits, one calendar for the whole team, and WhatsApp reminders before every visit.<\/p>\n      <span class=\"qc2-strip__go\">See the booking system<\/span><\/a><\/li>\n    <li class=\"qc2-strip__item\"><a href=\"\/how-we-work\/\">\n      <div class=\"qc2-pic qc2-cut qch-tile qc2--f-crm\">\n        <img loading=\"lazy\" class=\"qch-tile__phone\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-mobile-today.webp\" width=\"390\" height=\"844\" loading=\"lazy\" decoding=\"async\" alt=\"Our sales app on a phone: one rep's follow-ups for today, with a call to make and a quote to send\"><\/div>\n      <h3 class=\"qc2-title\">Startups<\/h3>\n      <p>A first version of your product, built in stages, so you can show it to real users early and grow from there.<\/p>\n      <span class=\"qc2-strip__go\">See how we work<\/span><\/a><\/li>\n  <\/ul>\n<\/div>\n<style data-qch=\"05\">\n\/* home page.css @05 Workday strip: \"Built around your daily work\" *\/\n\/* Six cards of one size: a square picture (four photographs, and two of our own screens, each standing on\n   its product's colour 10% from the top, the desktop screen also 10% from the left and running off the\n   right and bottom, the phone centred and cut at the foot), then the kind of business, one line and a\n   link. The row starts on the content edge under the heading and runs off the right of the page; at\n   rest it shows K whole cards and 40% of the next, so the cut always reads as \"more\" (K = 1 on phones,\n   2 on tablets, 3 from 64rem, 4 from 105rem), and at the end it ends on the content edge with 40% of\n   the card before it on the left. Native scroll and snap (touch, trackpad,\n   keyboard: the row takes focus and its links scroll into view), no scrollbar drawn. The previous \/ next\n   buttons close the line under the paragraph, on the content edge above the row; they page by K cards,\n   rest faded at each end, and their place is held when they are hidden (no script), so nothing moves. *\/\n.qc2.qch-strip .qch-strip__bar {\n  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n  gap: var(--qc2-s4) var(--qc2-s6); min-height: 3rem;\n}\n\/* Founder decision (26 Oct 2026): this one section runs the full width of the screen. Heading, buttons and\n   cards start and end on the page margin, not on the centred content column the other sections use. *\/\n.qc2.qch-strip { --qc2-edge: var(--qc2-margin); }\n.qc2.qch-strip > .qc2-wrap { max-width: none; }\n.qc2.qch-strip .qc2-strip__track {\n  --qch-k: 1; --qch-peek: 0.28;\n  \/* the gap between cards is the page margin, so a card that has scrolled past the content edge is gone\n     from the margin too (no sliver of it is left at the page's edge) *\/\n  gap: var(--qc2-margin);\n  \/* 8px above and below so the card links' focus ring is not cut by the row's own clip *\/\n  margin-top: calc(var(--qc2-s7) - 8px); padding-block: 8px;\n  scroll-snap-type: x mandatory; scrollbar-width: none;\n}\n.qc2.qch-strip .qc2-strip__track::-webkit-scrollbar { display: none; }\n.qc2.qch-strip .qc2-strip__track:focus-visible { outline-offset: -3px; }\n\/* the card: K whole cards and a share of the next fill the room from the content edge to the page's right *\/\n.qc2.qch-strip .qc2-strip__item {\n  flex: 0 0 calc((100cqw - var(--qc2-edge) - var(--qch-k) * var(--qc2-margin)) \/ (var(--qch-k) + var(--qch-peek)));\n  min-width: 0; scroll-snap-align: start;\n}\n\/* one square frame for every picture: the photo fills it from its focal point (a 4:5 photo in flow would\n   stretch an aspect-ratio box to its own height, so it is placed over the frame instead) *\/\n.qc2.qch-strip .qc2-strip__item .qc2-pic { aspect-ratio: 1 \/ 1; min-height: 0; }\n.qc2.qch-strip .qc2-strip__item .qc2-pic:not(.qch-tile) > img { position: absolute; inset: 0; width: 100%; height: 100%; }\n@container qc2 (min-width: 40rem) { .qc2.qch-strip .qc2-strip__track { --qch-k: 2; --qch-peek: 0.4; } }\n@container qc2 (min-width: 64rem) { .qc2.qch-strip .qc2-strip__track { --qch-k: 3; } }\n@container qc2 (min-width: 105rem) { .qc2.qch-strip .qc2-strip__track { --qch-k: 4; } }\n\/* our screens on their product's colour *\/\n.qc2.qch-strip .qch-tile { background: var(--qc2-bg); }\n.qc2.qch-strip .qch-tile > img { position: absolute; top: 10%; max-width: none; height: auto; margin: 0; object-fit: fill; }\n.qc2.qch-strip .qch-tile > .qch-tile__desk  { left: 10%; width: 160%; aspect-ratio: 16 \/ 10; }\n.qc2.qch-strip .qch-tile > .qch-tile__phone { left: 24%; width: 52%; aspect-ratio: 390 \/ 844; border-radius: 7.2% \/ 3.33%; }\n\/* the buttons: Ink pills with the mark's two chevrons (Paper pills on the dark site, v2-dark.css 5);\n   each rests faded at its end *\/\n.qc2.qch-strip .qch-strip__nav { display: flex; gap: var(--qc2-s3); margin-left: auto; }\n.qc2.qch-strip .qch-strip__nav[hidden] { display: none; }\n.qc2.qch-strip .qch-strip__btn {\n  display: grid; place-items: center; flex: none; width: 3rem; height: 3rem; padding: 0; margin: 0;\n  border: 0; border-radius: 999px; background: var(--qc2-ink); color: var(--qc2-paper); cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  transition: transform var(--qc2-t-press) var(--qc2-ease-out), background-color var(--qc2-t-hover) ease, opacity var(--qc2-t-hover) ease;\n}\n.qc2.qch-strip .qch-strip__btn:is(:hover, :focus-visible) { background: var(--qc2-ink-800); color: var(--qc2-paper); }\n.qc2.qch-strip .qch-strip__btn:active { transform: scale(0.97); }\n.qc2.qch-strip .qch-strip__btn:disabled { opacity: 0.28; cursor: default; background: var(--qc2-ink); transform: none; }\n.qc2.qch-strip .qch-strip__btn:focus-visible { outline: 3px solid var(--qc2-focus); outline-offset: 3px; }\n.qc2.qch-strip .qch-strip__btn svg { width: 10px; height: 17px; display: block; }\n.qc2.qch-strip .qch-strip__btn path { fill: none; stroke: currentColor; stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; }\n\/* The strip's pictures sit inside the sideways-scrolling track, so view() would follow the track (which\n   never scrolls vertically) and the cut would never run. The track names its own view timeline on the\n   page's scroll and the pictures open on that: the row opens along the 45-degree cut as it comes up.\n   (On the dark site reveal.css retires the cut and reveal.js plays each card as it comes into view.) *\/\n@supports (animation-timeline: view()) {\n  @media (prefers-reduced-motion: no-preference) {\n    .qc2.qch-strip .qc2-strip__track { view-timeline: --qch-strip block; }\n    .qc2.qch-strip .qc2-strip__item .qc2-cut { animation-timeline: --qch-strip; animation-range: entry 5% cover 40%; }\n  }\n}\n@media (prefers-reduced-motion: reduce) {\n  .qc2.qch-strip .qch-strip__btn { transition: none; }\n}\n<\/style>\n<script>\n\/* Workday strip: the previous \/ next buttons page the row by the cards wholly in view (touch, trackpad\n   and keyboard scroll it natively). Shown only when the row overflows; each button rests disabled at its\n   end, and a button disabled while it has the focus hands the focus to the other one. A card tabbed to\n   while it is cut off at either side scrolls in to the content edge. *\/\ndocument.querySelectorAll('.qch-strip').forEach(function (strip) {\n  var track = strip.querySelector('.qc2-strip__track'), nav = strip.querySelector('.qch-strip__nav');\n  if (!track || !nav) return;\n  var prev = nav.querySelector('[data-dir=\"-1\"]'), next = nav.querySelector('[data-dir=\"1\"]');\n  var items = Array.prototype.slice.call(track.children);\n  var still = window.matchMedia('(prefers-reduced-motion: reduce)');\n  if (!prev || !next || !items.length) return;\n  function max() { return track.scrollWidth - track.clientWidth; }\n  \/* the content edge: where a card stands when it is first in the row *\/\n  function edge() { return track.getBoundingClientRect().left + (parseFloat(getComputedStyle(track).scrollPaddingLeft) || 0); }\n  \/* the first card that starts on or after the content edge, and how many cards are wholly in view *\/\n  function first() {\n    var at = edge() - 2;\n    for (var i = 0; i < items.length; i++) if (items[i].getBoundingClientRect().left >= at) return i;\n    return items.length - 1;\n  }\n  function inView() {\n    var lo = edge() - 2, hi = track.getBoundingClientRect().right + 2, n = 0;\n    items.forEach(function (li) { var r = li.getBoundingClientRect(); if (r.left >= lo && r.right <= hi) n++; });\n    return Math.max(1, n);\n  }\n  function go(dir) {\n    var to = Math.max(0, Math.min(items.length - 1, first() + dir * inView()));\n    var x = items[to].getBoundingClientRect().left - edge() + track.scrollLeft;\n    track.scrollTo({ left: Math.max(0, Math.min(max(), Math.round(x))), behavior: still.matches ? 'auto' : 'smooth' });\n  }\n  function update() {\n    var m = max(), x = track.scrollLeft, atStart = x < 2, atEnd = x > m - 2;\n    nav.hidden = m < 2;\n    if (atEnd && !atStart && document.activeElement === next) prev.focus({ preventScroll: true });\n    if (atStart && !atEnd && document.activeElement === prev) next.focus({ preventScroll: true });\n    prev.disabled = atStart;\n    next.disabled = atEnd;\n  }\n  \/* a card reached by the keyboard (Tab, Shift+Tab) that is not wholly in view is brought to the content\n     edge, as far as the row goes; focus from a pointer is left alone, so a click on the peeking card opens it *\/\n  track.addEventListener('focusin', function (e) {\n    var li = e.target.closest ? e.target.closest('.qc2-strip__item') : null, i = items.indexOf(li);\n    if (i < 0) return;\n    var kb = true;\n    try { kb = e.target.matches(':focus-visible'); } catch (err) { \/* old browser: treat as keyboard *\/ }\n    if (!kb) return;\n    var r = items[i].getBoundingClientRect(), lo = edge() - 2, hi = track.getBoundingClientRect().right + 2;\n    if (r.left >= lo && r.right <= hi) return;\n    var x = r.left - edge() + track.scrollLeft;\n    track.scrollTo({ left: Math.max(0, Math.min(max(), Math.round(x))), behavior: still.matches ? 'auto' : 'smooth' });\n  });\n  prev.addEventListener('click', function () { go(-1); });\n  next.addEventListener('click', function () { go(1); });\n  track.addEventListener('scroll', update, { passive: true });\n  if ('ResizeObserver' in window) new ResizeObserver(update).observe(track);\n  else window.addEventListener('resize', update);\n  update();\n});\n<\/script>\n<style data-qc2-guard>\n\/* make_v2.py: the margin rules above, one id stronger than Elementor's figure reset (kit-v2.css 2) *\/\n.qc2.qch-strip .qc2-strip__track:not(#qc2-guard) { margin-top: calc(var(--qc2-s7) - 8px); }\n.qc2.qch-strip .qch-tile > img:not(#qc2-guard) { margin: 0; }\n.qc2.qch-strip .qch-strip__nav:not(#qc2-guard) { margin-left: auto; }\n.qc2.qch-strip .qch-strip__btn:not(#qc2-guard) { margin: 0; }\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-4e73c282 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"4e73c282\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7352879e elementor-widget elementor-widget-html\" data-id=\"7352879e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--volt qc2-volt qch-volt\">\n  <div class=\"qc2-wrap\">\n    <div class=\"qc2-volt__frame\">\n      <svg class=\"qc2-volt__chev qc2-volt__chev--l\" viewBox=\"0 0 44 76\" aria-hidden=\"true\"><path d=\"M38 6 L6 38 L38 70\"\/><\/svg>\n      <div class=\"qc2-head\">\n        <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>Honest advice<\/p>\n        <p class=\"qc2-h qc2-volt__say\">We'll tell you if you don't need us.<\/p>\n      <\/div>\n      <svg class=\"qc2-volt__chev qc2-volt__chev--r\" viewBox=\"0 0 44 76\" aria-hidden=\"true\"><path d=\"M6 6 L38 38 L6 70\"\/><\/svg>\n    <\/div>\n    <p class=\"qc2-lead qc2-volt__more\">If a ready-made tool is enough, we'll say so before you spend money.<\/p>\n    <div class=\"qc2-actions\">\n      <a class=\"qc2-btn\" href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.%20I%20need%20help%20deciding.\">Ask us what fits<\/a>\n    <\/div>\n  <\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-365213a3 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"365213a3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-579c7825 elementor-widget elementor-widget-html\" data-id=\"579c7825\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--paper qc2-rail qc2-sec qch-rail\" id=\"how-we-work\">\n  <div class=\"qc2-wrap qc2-grid\">\n    <div class=\"qc2-rail__hold\">\n      <div class=\"qc2-head\">\n        <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>The process<\/p>\n        <div class=\"qch-rail__top\">\n          <h2 class=\"qc2-title\">Your project in four steps<\/h2>\n          <a class=\"qc2-link\" href=\"\/how-we-work\/\">See how we work<\/a>\n        <\/div>\n      <\/div>\n      <div class=\"qc2-rail__frame\">\n        <figure class=\"qc2-pic is-on\" style=\"--qc2-focus: 50% 42%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-phone-held-first-message-800.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-phone-held-first-message-800.webp 800w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-phone-held-first-message.webp 1600w\" sizes=\"auto, 50vw\" width=\"1600\" height=\"2000\" loading=\"lazy\" decoding=\"async\" alt=\"A hand holds up a phone in a sunny doorway; on screen, a chat with Qcoders: a first message, a voice note, and a reply asking for a short call tomorrow\">\n        <\/figure>\n        <figure class=\"qc2-pic\" style=\"--qc2-focus: 30% 40%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-first-meeting-owner-explains-1200.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-first-meeting-owner-explains-1200.webp 1200w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-first-meeting-owner-explains.webp 2400w\" sizes=\"auto, 50vw\" width=\"2400\" height=\"1600\" loading=\"lazy\" decoding=\"async\" alt=\"A grey-haired business owner explains how his business works while a younger man listens and takes notes\">\n        <\/figure>\n        <figure class=\"qc2-pic\" style=\"--qc2-focus: 62% 40%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-hands-laptop-crm-pipeline-1200.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-hands-laptop-crm-pipeline-1200.webp 1200w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-hands-laptop-crm-pipeline.webp 2400w\" sizes=\"auto, 50vw\" width=\"2400\" height=\"1600\" loading=\"lazy\" decoding=\"async\" alt=\"Hands typing on a laptop on a light oak desk; the screen shows the deal board of our sales CRM\">\n        <\/figure>\n        <figure class=\"qc2-pic\" style=\"--qc2-focus: 50% 35%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-baker-orders-tablet-800.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-baker-orders-tablet-800.webp 800w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-baker-orders-tablet.webp 1600w\" sizes=\"auto, 50vw\" width=\"1600\" height=\"2000\" loading=\"lazy\" decoding=\"async\" alt=\"A grey-haired baker in an apron smiles at a tablet behind his counter, a tray of croissants in front of him\">\n        <\/figure>\n      <\/div>\n    <\/div>\n    <ol class=\"qc2-rail__steps\">\n      <li class=\"qc2-rail__step\">\n        <figure class=\"qc2-pic qc2-rail__inline qc2-cut\" style=\"--qc2-focus: 50% 40%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-phone-held-first-message-800.webp\" width=\"800\" height=\"1000\" loading=\"lazy\" decoding=\"async\" alt=\"A hand holds up a phone showing a first chat with Qcoders and a reply asking for a short call\">\n        <\/figure>\n        <h3 class=\"qc2-h\">Listen<\/h3>\n        <p>It starts with a message, then a short call about your business and what takes your time. So you don't need to know what to ask for.<\/p>\n        <p class=\"qc2-rail__get\"><strong>You get<\/strong> plain language: every technical word explained, or removed.<\/p>\n      <\/li>\n      <li class=\"qc2-rail__step\">\n        <figure class=\"qc2-pic qc2-rail__inline qc2-cut\" style=\"--qc2-focus: 26% 40%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-first-meeting-owner-explains-1200.webp\" width=\"1200\" height=\"800\" loading=\"lazy\" decoding=\"async\" alt=\"A business owner explains how his business works while a younger man takes notes\">\n        <\/figure>\n        <h3 class=\"qc2-h\">Decide<\/h3>\n        <p>New system or ready-made tool? Then you see the options side by side, in plain words, with a clear recommendation.<\/p>\n        <p class=\"qc2-rail__get\"><strong>You get<\/strong> a written proposal: scope, stages, cost and timeline.<\/p>\n      <\/li>\n      <li class=\"qc2-rail__step\">\n        <figure class=\"qc2-pic qc2-rail__inline qc2-cut\" style=\"--qc2-focus: 64% 40%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-v2-hands-laptop-crm-pipeline-1200.webp\" width=\"1200\" height=\"800\" loading=\"lazy\" decoding=\"async\" alt=\"Hands typing on a laptop that shows the deal board of our sales CRM\">\n        <\/figure>\n        <h3 class=\"qc2-h\">Build<\/h3>\n        <p>Your system is built in stages, and you see each one working, so progress is never a guess.<\/p>\n        <p class=\"qc2-rail__get\"><strong>You get<\/strong> something working early, and no surprises at the end.<\/p>\n      <\/li>\n      <li class=\"qc2-rail__step\">\n        <figure class=\"qc2-pic qc2-rail__inline qc2-cut\" style=\"--qc2-focus: 50% 35%\">\n          \n          <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-baker-orders-tablet-800.webp\" width=\"800\" height=\"1000\" loading=\"lazy\" decoding=\"async\" alt=\"A baker smiles at a tablet behind his counter\">\n        <\/figure>\n        <h3 class=\"qc2-h\">Stay<\/h3>\n        <p>Your team learns the system during handover. After that it stays hosted, updated and fixed, if you want that.<\/p>\n        <p class=\"qc2-rail__get\"><strong>You get<\/strong> a partner after launch, on a support plan that says what it covers.<\/p>\n      <\/li>\n    <\/ol>\n  <\/div>\n<\/div>\n<style data-qch=\"07\">\n\/* home page.css @07 Process rail: \"Your project in four steps\" *\/\n\/* The held side opens with the section's name and its link at opposite ends of one line. Each step:\n   the word at heading size, what happens, and what you get (its first words in the full ink). The home\n   version is the short one: steps closer together than v2's default (about a third of a screen each,\n   not half) and a wider text column, so the section reads in about two screens instead of three. *\/\n.qc2.qch-rail .qch-rail__top { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--qc2-s3) var(--qc2-s6); }\n.qc2.qch-rail .qc2-rail__step > p { max-width: 46ch; }\n.qc2.qch-rail .qc2-rail__step > h3 + p { margin-top: var(--qc2-s5); }\n.qc2.qch-rail .qc2-rail__get strong { color: var(--qc2-fg); font-weight: 500; }\n@container qc2 (max-width: 63.99rem) {\n  .qc2.qch-rail .qc2-rail__step > .qc2-rail__inline { aspect-ratio: 4 \/ 5; margin-inline: calc(-1 * var(--qc2-margin)); margin-bottom: var(--qc2-s6); }\n  .qc2.qch-rail .qc2-rail__steps { margin-top: var(--qc2-s7); }\n}\n@container qc2 (min-width: 40rem) and (max-width: 63.99rem) {\n  .qc2.qch-rail .qc2-rail__step > .qc2-rail__inline { aspect-ratio: 3 \/ 2; }\n}\n@container qc2 (min-width: 64rem) {\n  .qc2.qch-rail .qc2-rail__steps { grid-column: 8 \/ -1; padding-block: 6svh 22svh; }\n  .qc2.qch-rail .qc2-rail__step { min-height: 36svh; }\n}\n<\/style>\n<style data-qc2-guard>\n\/* make_v2.py: the margin rules above, one id stronger than Elementor's figure reset (kit-v2.css 2) *\/\n.qc2.qch-rail .qc2-rail__step > h3 + p:not(#qc2-guard) { margin-top: var(--qc2-s5); }\n@container qc2 (max-width: 63.99rem) { .qc2.qch-rail .qc2-rail__step > .qc2-rail__inline:not(#qc2-guard) { margin-inline: calc(-1 * var(--qc2-margin)); margin-bottom: var(--qc2-s6); } }\n@container qc2 (max-width: 63.99rem) { .qc2.qch-rail .qc2-rail__steps:not(#qc2-guard) { margin-top: var(--qc2-s7); } }\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e533414 e-con-full elementor-hidden-mobile e-flex e-con e-parent\" data-id=\"e533414\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-285381f0 elementor-widget elementor-widget-html\" data-id=\"285381f0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\n<div class=\"qc2 qc2--ink qc2-line qch-line\" id=\"contact\">\n  <div class=\"qc2-wrap qc2-grid\">\n    <div class=\"qc2-line__text qc2-stack\">\n      <p class=\"qc2-label\"><span aria-hidden=\"true\">\/\/ <\/span>Contact<\/p>\n      <h2 class=\"qc2-h\">Tell us what takes your time<\/h2>\n      <p class=\"qc2-lead qc2-muted\">Pick what you need, then we'll write the first message for you. No tech words needed.<\/p>\n      <fieldset class=\"qc2-pick\">\n        <legend>What do you need?<\/legend>\n        <div class=\"qc2-pick__opts\">\n          <label><input type=\"radio\" name=\"qch-need\" value=\"a website\"><span>Website<\/span><\/label>\n          <label><input type=\"radio\" name=\"qch-need\" value=\"an online store\"><span>Online store<\/span><\/label>\n          <label><input type=\"radio\" name=\"qch-need\" value=\"a mobile app\"><span>App<\/span><\/label>\n          <label><input type=\"radio\" name=\"qch-need\" value=\"a business system\"><span>Business system<\/span><\/label>\n          <label><input type=\"radio\" name=\"qch-need\" value=\"an AI assistant\"><span>AI<\/span><\/label>\n          <label><input type=\"radio\" name=\"qch-need\" value=\"help deciding\"><span>Not sure yet<\/span><\/label>\n        <\/div>\n      <\/fieldset>\n      <div class=\"qc2-actions\">\n        <a class=\"qc2-btn\" data-qc2-wa href=\"https:\/\/wa.me\/966507600156?text=Hi%20Qcoders%2C%20I%20found%20you%20on%20your%20website.\">Message us on WhatsApp<\/a>\n        <a class=\"qc2-link\" href=\"mailto:info@qcoders.com\">info@qcoders.com<\/a>\n      <\/div>\n    <\/div>\n    <figure class=\"qc2-pic qc2-line__pic qc2-cut\">\n      \n      <img loading=\"lazy\" src=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-phone-held-close.webp\" srcset=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-phone-held-close-640.webp 640w, https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-photo-phone-held-close.webp 1000w\" sizes=\"auto, (min-width: 1024px) 40vw, 100vw\" width=\"1000\" height=\"1250\" loading=\"lazy\" decoding=\"async\" alt=\"A hand holds up a phone in a sunny doorway\">\n      <div class=\"qc2-line__screen\" data-ui aria-hidden=\"true\">\n        <div class=\"qc2-line__bar\">\n          <svg viewBox=\"7 16 115 97\"><g fill=\"none\" stroke-width=\"12\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M51 28 L19 60 L51 92\" stroke=\"#0A0E17\"\/><path d=\"M69 28 L101 60 L69 92\" stroke=\"#0A0E17\"\/><path d=\"M97 88 L110 101\" stroke=\"#6E9400\"\/><\/g><\/svg>\n          Qcoders\n        <\/div>\n        <div class=\"qc2-line__msgs\">\n          <p class=\"qc2-line__bubble\" data-qc2-wa-text>Hi Qcoders, I found you on your website.<\/p>\n        <\/div>\n        <div class=\"qc2-line__compose\">Message<\/div>\n      <\/div>\n    <\/figure>\n  <\/div>\n<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Code that runs your business. Custom software for your business: websites, online stores, apps, business systems and AI. For any size, anywhere. WhatsApp us See work \/\/ Our work Built andrunning. 06 projects 01 \/ Systems Sales CRM Every lead, call and WhatsApp chat in one place. 02 \/ Stores Fashion online store A modern [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-19","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Custom software for business, built around you | Qcoders<\/title>\n<meta name=\"description\" content=\"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/qcoders.com\/ar\/\" \/>\n<meta property=\"og:locale\" content=\"ar_AR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Custom software for business, built around you | Qcoders\" \/>\n<meta property=\"og:description\" content=\"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/qcoders.com\/ar\/\" \/>\n<meta property=\"og:site_name\" content=\"Qcoders\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-02T07:00:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"15 \u062f\u0642\u064a\u0642\u0629\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/\",\"url\":\"https:\\\/\\\/qcoders.com\\\/\",\"name\":\"Custom software for business, built around you | Qcoders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/qcoders.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/qc-work-sales-crm-pipeline.webp\",\"datePublished\":\"2026-10-25T14:07:04+00:00\",\"dateModified\":\"2026-11-02T07:00:53+00:00\",\"description\":\"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#breadcrumb\"},\"inLanguage\":\"ar\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/qcoders.com\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ar\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/#primaryimage\",\"url\":\"https:\\\/\\\/qcoders.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/qc-work-sales-crm-pipeline.webp\",\"contentUrl\":\"https:\\\/\\\/qcoders.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/qc-work-sales-crm-pipeline.webp\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/qcoders.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Home\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/#website\",\"url\":\"https:\\\/\\\/qcoders.com\\\/\",\"name\":\"Qcoders\",\"description\":\"Code that runs your business.\",\"publisher\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/qcoders.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ar\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/#organization\",\"name\":\"Qcoders\",\"url\":\"https:\\\/\\\/qcoders.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ar\",\"@id\":\"https:\\\/\\\/qcoders.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/qcoders.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/qcoders-logo-qcoders-2026.png\",\"contentUrl\":\"https:\\\/\\\/qcoders.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/qcoders-logo-qcoders-2026.png\",\"width\":2000,\"height\":398,\"caption\":\"Qcoders\"},\"image\":{\"@id\":\"https:\\\/\\\/qcoders.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Custom software for business, built around you | Qcoders","description":"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/qcoders.com\/ar\/","og_locale":"ar_AR","og_type":"article","og_title":"Custom software for business, built around you | Qcoders","og_description":"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.","og_url":"https:\/\/qcoders.com\/ar\/","og_site_name":"Qcoders","article_modified_time":"2026-11-02T07:00:53+00:00","og_image":[{"url":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp","type":"","width":"","height":""}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"15 \u062f\u0642\u064a\u0642\u0629"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/qcoders.com\/","url":"https:\/\/qcoders.com\/","name":"Custom software for business, built around you | Qcoders","isPartOf":{"@id":"https:\/\/qcoders.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/qcoders.com\/#primaryimage"},"image":{"@id":"https:\/\/qcoders.com\/#primaryimage"},"thumbnailUrl":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp","datePublished":"2026-10-25T14:07:04+00:00","dateModified":"2026-11-02T07:00:53+00:00","description":"Custom software for business, built around how you work: websites, online stores, apps and business systems, for companies of any size, anywhere.","breadcrumb":{"@id":"https:\/\/qcoders.com\/#breadcrumb"},"inLanguage":"ar","potentialAction":[{"@type":"ReadAction","target":["https:\/\/qcoders.com\/"]}]},{"@type":"ImageObject","inLanguage":"ar","@id":"https:\/\/qcoders.com\/#primaryimage","url":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp","contentUrl":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qc-work-sales-crm-pipeline.webp"},{"@type":"BreadcrumbList","@id":"https:\/\/qcoders.com\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/qcoders.com\/"},{"@type":"ListItem","position":2,"name":"Home"}]},{"@type":"WebSite","@id":"https:\/\/qcoders.com\/#website","url":"https:\/\/qcoders.com\/","name":"Qcoders","description":"\u0628\u0631\u0645\u062c\u064a\u0627\u062a \u062a\u0634\u063a\u0651\u0644 \u0623\u0639\u0645\u0627\u0644\u0643.","publisher":{"@id":"https:\/\/qcoders.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/qcoders.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ar"},{"@type":"Organization","@id":"https:\/\/qcoders.com\/#organization","name":"Qcoders","url":"https:\/\/qcoders.com\/","logo":{"@type":"ImageObject","inLanguage":"ar","@id":"https:\/\/qcoders.com\/#\/schema\/logo\/image\/","url":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qcoders-logo-qcoders-2026.png","contentUrl":"https:\/\/qcoders.com\/wp-content\/uploads\/2026\/10\/qcoders-logo-qcoders-2026.png","width":2000,"height":398,"caption":"Qcoders"},"image":{"@id":"https:\/\/qcoders.com\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/pages\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":37,"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/pages\/19\/revisions"}],"predecessor-version":[{"id":1100,"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/pages\/19\/revisions\/1100"}],"wp:attachment":[{"href":"https:\/\/qcoders.com\/ar\/wp-json\/wp\/v2\/media?parent=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}