<!DOCTYPE html>
<html lang="es-AR" translate="no">
  <head>
    <meta charset="UTF-8" />
    <meta name="google" content="notranslate" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- WEB-873: the title and description below, and their og:/twitter:
         twins, are copies of BRAND_TITLE / BRAND_DESCRIPTION in
         `src/config/brand.ts`. This is a static document, so they are copies
         rather than an interpolation, and
         `src/__tests__/seo/brandCopySingleSource.test.ts` compares them byte
         for byte. Edit the constants and re-run that suite; do not edit these
         strings alone. -->
    <title>Muovi | Cualquier tarea: electricistas, plomeros, gasistas y más</title>
    <meta name="description" content="Publicá cualquier tarea en Argentina y recibí ofertas de profesionales y trabajadores verificados: electricistas, plomeros, gasistas, limpieza y más." />
    <meta name="author" content="Muovi" />
    <meta name="keywords" content="publicar una tarea, limpieza, mudanzas, armado de muebles, fletes, electricistas, plomeros, gasistas, pintores, aire acondicionado, cerrajeros, albañiles, profesionales verificados, servicios, Argentina, CABA" />
    <!-- WEB-754: NO CANONICAL HERE, deliberately.
         This file is the SPA shell. Vike prerenders its own document per route
         and emits a self-canonical there, so this tag never reached a
         prerendered page — it only ever shipped inside `_spa-fallback.html`,
         which `vercel.json`'s catch-all serves for every UNMATCHED path. The
         effect was that every soft 404 told crawlers it was the homepage:
         verified live on 2026-08-20, `/ruta-que-no-existe` returned a
         canonical link pointing at the site root.
         react-helmet-async only replaces tags it owns (`link[data-rh]`), so a
         component could not override this one by declaring its own.
         WEB-428 patched the symptom on the `/precios/` and `/guias/` hubs —
         see `seoMeta.test.ts`, "NOT the homepage canonical the SPA fallback
         baked". This removes the source. -->
    <link rel="llms-txt" href="/llms.txt" />

    <meta property="og:title" content="Muovi | Cualquier tarea: electricistas, plomeros, gasistas y más" />
    <meta property="og:description" content="Publicá cualquier tarea en Argentina y recibí ofertas de profesionales y trabajadores verificados: electricistas, plomeros, gasistas, limpieza y más." />
    <meta property="og:type" content="website" />
    <meta property="og:image" content="https://www.muovi.com.ar/og.png?v=2" />
    <meta property="og:url" content="https://www.muovi.com.ar/" />
    <meta property="og:site_name" content="Muovi" />

    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "Organization",
      "name": "Muovi",
      "url": "https://www.muovi.com.ar",
      "logo": "https://www.muovi.com.ar/og.png?v=2"
    }
    </script>
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "WebSite",
      "name": "Muovi",
      "url": "https://www.muovi.com.ar"
    }
    </script>

    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="Muovi | Cualquier tarea: electricistas, plomeros, gasistas y más" />
    <meta name="twitter:description" content="Publicá cualquier tarea en Argentina y recibí ofertas de profesionales y trabajadores verificados: electricistas, plomeros, gasistas, limpieza y más." />
    <meta name="twitter:image" content="https://www.muovi.com.ar/og.png?v=2" />
    <meta name="facebook-domain-verification" content="vt176iaijlhgqg5pjl5dte7yufwzf9" />
    <link rel="icon" href="/favicon.ico">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    <link rel="manifest" href="/site.webmanifest">
    <meta name="theme-color" content="#16A34A">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link rel="preconnect" href="https://mhuxdpulcwkfobteuero.supabase.co">
    <link rel="preload" as="font" type="font/woff2" crossorigin href="https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_fjbvMwCp50KnMa2JL7W0Q5n-wU.woff2">
    <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Manrope:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Manrope:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"></noscript>

    <!--
      WEB-579: the GTM bootstrap is injected at build time by the
      `muovi-gtm-bootstrap` Vite plugin (vite.config.ts) from the single
      source in `src/analytics/install/gtmBootstrap.ts` — the same module the
      Vike prerendered shell (pages/) and the api/ SSR renderers consume. Do
      NOT re-inline the snippet here; edit the module instead.

      The MOB-82 deferral it emits is unchanged: the inline dataLayer +
      gtm.start push stays synchronous so early pushes queue in order, while
      the external gtm.js tag-loader is appended on window 'load' so it does
      not compete with LCP on mobile.
    -->
    <!-- Google Tag Manager -->
    <script>
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
      (function (w, d, s, l, i) {
        function loadGtm() {
          var f = d.getElementsByTagName(s)[0];
          var j = d.createElement(s);
          var dl = l != 'dataLayer' ? '&l=' + l : '';
          j.async = true;
          j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
          f.parentNode.insertBefore(j, f);
        }
        if (d.readyState === 'complete') {
          // 'load' already fired — schedule at the next idle moment.
          setTimeout(loadGtm, 0);
        } else {
          w.addEventListener('load', loadGtm, { once: true });
        }
      })(window, document, 'script', 'dataLayer', 'GTM-PF2GHWZP');
    </script>
    <!-- End Google Tag Manager -->
    <script type="module" crossorigin src="/assets/index-BQC0X4J8.js"></script>
    <link rel="modulepreload" crossorigin href="/assets/vendor-lucide-BKHGALhO.js">
    <link rel="modulepreload" crossorigin href="/assets/vendor-supabase-CH6LFsvG.js">
    <link rel="modulepreload" crossorigin href="/assets/vendor-radix-D3BwhwX3.js">
    <link rel="modulepreload" crossorigin href="/assets/vendor-sentry-JrdECF_F.js">
    <link rel="stylesheet" crossorigin href="/assets/index-CB_amFkE.css">
  </head>

  <body>
    <!-- Google Tag Manager (noscript) -->
    <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PF2GHWZP"
    height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
    <!-- End Google Tag Manager (noscript) -->

    <div id="root"></div>
  </body>
</html>
