{"id":8246,"date":"2026-08-06T17:16:18","date_gmt":"2026-08-06T17:16:18","guid":{"rendered":"https:\/\/www.frenchtechberlin.com\/non-classifiee\/personnalisez-votre-visuel-de-participation\/"},"modified":"2026-08-06T21:14:22","modified_gmt":"2026-08-06T21:14:22","slug":"personnalisez-votre-visuel-de-participation","status":"publish","type":"post","link":"https:\/\/www.frenchtechberlin.com\/fr\/recompenses\/personnalisez-votre-visuel-de-participation\/","title":{"rendered":"Personnalisez votre visuel de participation"},"content":{"rendered":"\n<style data-wp-block-html=\"css\">\n#ft-awards-generator,\n#ft-awards-generator * {\n    box-sizing: border-box;\n}\n\n#ft-awards-generator {\n    width: 100%;\n    max-width: 1500px;\n    margin: 45px auto;\n    padding: 0 22px;\n    color: #191929;\n    font-family: Arial, Helvetica, sans-serif;\n}\n\n#ft-awards-generator [hidden] {\n    display: none !important;\n}\n\n#ft-awards-generator .ft-generator-header {\n    margin-bottom: 30px;\n}\n\n#ft-awards-generator .ft-generator-eyebrow {\n    display: inline-block;\n    margin-bottom: 9px;\n    color: #e30613;\n    font-size: 12px;\n    font-weight: 800;\n    letter-spacing: 0.13em;\n    text-transform: uppercase;\n}\n\n#ft-awards-generator .ft-generator-header h2 {\n    margin: 0 0 8px;\n    color: #191929;\n    font-size: clamp(29px, 3.5vw, 43px);\n    font-weight: 800;\n    line-height: 1.1;\n    letter-spacing: -0.025em;\n}\n\n#ft-awards-generator .ft-generator-header p {\n    max-width: 760px;\n    margin: 0;\n    color: #5e5e6b;\n    font-size: 16px;\n    line-height: 1.55;\n}\n\n#ft-awards-generator .ft-generator-layout {\n    display: grid;\n    grid-template-columns: minmax(330px, 420px) minmax(0, 1fr);\n    gap: 34px;\n    align-items: start;\n}\n\n#ft-awards-generator .ft-controls {\n    display: grid;\n    gap: 21px;\n    padding: 27px;\n    border: 1px solid #e2e2e8;\n    border-radius: 14px;\n    background: #f7f7f9;\n    box-shadow: 0 12px 40px rgba(20, 20, 40, 0.05);\n}\n\n#ft-awards-generator .ft-field {\n    display: grid;\n    gap: 8px;\n}\n\n#ft-awards-generator .ft-field-heading {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 9px;\n    min-height: 30px;\n}\n\n#ft-awards-generator label {\n    color: #20202d;\n    font-size: 15px;\n    font-weight: 750;\n    line-height: 1.3;\n}\n\n#ft-awards-generator input[type=\"text\"],\n#ft-awards-generator input[type=\"file\"],\n#ft-awards-generator select {\n    width: 100%;\n    min-height: 49px;\n    padding: 11px 13px;\n    border: 1px solid #d7d7df;\n    border-radius: 8px;\n    outline: none;\n    background: #ffffff;\n    color: #242432;\n    font: inherit;\n}\n\n#ft-awards-generator input[type=\"text\"]::placeholder {\n    color: #777777;\n    opacity: 1;\n}\n\n#ft-awards-generator input[type=\"file\"] {\n    padding: 8px;\n    cursor: pointer;\n}\n\n#ft-awards-generator input[type=\"file\"]::file-selector-button {\n    min-height: 34px;\n    margin-right: 9px;\n    padding: 7px 11px;\n    border: 0;\n    border-radius: 6px;\n    background: #232051;\n    color: #ffffff;\n    font-weight: 700;\n    cursor: pointer;\n}\n\n#ft-awards-generator input:focus,\n#ft-awards-generator select:focus {\n    border-color: #e30613;\n    box-shadow: 0 0 0 3px rgba(227, 6, 19, 0.12);\n}\n\n#ft-awards-generator small {\n    color: #757582;\n    font-size: 12px;\n    line-height: 1.45;\n}\n\n\/* Photo tools *\/\n\n#ft-awards-generator .ft-photo-tools {\n    display: inline-flex;\n    align-items: center;\n    gap: 3px;\n    flex: none;\n}\n\n#ft-awards-generator .ft-photo-tools button {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 27px;\n    height: 27px;\n    min-height: 27px;\n    padding: 0;\n    border: 1px solid #d5d5dd;\n    border-radius: 6px;\n    background: #ffffff;\n    color: #232051;\n    font-family: Arial, Helvetica, sans-serif;\n    font-size: 17px;\n    font-weight: 800;\n    line-height: 1;\n    cursor: pointer;\n}\n\n#ft-awards-generator .ft-photo-tools button:hover {\n    border-color: #232051;\n    background: #eeeef4;\n}\n\n#ft-awards-generator .ft-photo-tools button:focus-visible {\n    outline: 2px solid #e30613;\n    outline-offset: 1px;\n}\n\n#ft-awards-generator .ft-photo-tools button:disabled {\n    cursor: default;\n    opacity: 0.35;\n}\n\n#ft-awards-generator .ft-tool-divider {\n    width: 1px;\n    height: 20px;\n    margin: 0 2px;\n    background: #d5d5dd;\n}\n\n\/* Announcement design controls *\/\n\n#ft-awards-generator .ft-announcement-styles {\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n    flex: none;\n}\n\n#ft-awards-generator .ft-announcement-styles button {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 31px;\n    height: 31px;\n    min-height: 31px;\n    padding: 0;\n    border: 1px solid #d5d5dd;\n    border-radius: 7px;\n    background: #ffffff;\n    cursor: pointer;\n}\n\n#ft-awards-generator .ft-announcement-styles button:hover {\n    border-color: #232051;\n    background: #f0f0f5;\n}\n\n#ft-awards-generator .ft-announcement-styles button.is-active {\n    border-color: #e30613;\n    box-shadow: 0 0 0 2px rgba(227, 6, 19, 0.13);\n}\n\n#ft-awards-generator .ft-announcement-styles button:focus-visible {\n    outline: 2px solid #e30613;\n    outline-offset: 1px;\n}\n\n#ft-awards-generator .ft-style-preview {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 20px;\n    height: 16px;\n    font-size: 9px;\n    font-weight: 900;\n    line-height: 1;\n}\n\n#ft-awards-generator .ft-style-editorial {\n    color: #232051;\n    border-bottom: 2px solid #c9a916;\n}\n\n#ft-awards-generator .ft-style-classic {\n    border-radius: 3px;\n    background: #e30613;\n    color: #ffffff;\n}\n\n#ft-awards-generator .ft-style-outline {\n    border: 2px solid #232051;\n    border-radius: 3px;\n    color: #232051;\n}\n\n#ft-awards-generator .ft-style-card {\n    border-radius: 2px;\n    background: #ffffff;\n    color: #232051;\n    box-shadow: 1px 2px 4px rgba(20, 20, 40, 0.28);\n    transform: rotate(-5deg);\n}\n\n\n\n\/* Main buttons *\/\n\n#ft-awards-generator .ft-actions {\n    display: grid;\n    grid-template-columns: minmax(0, 1fr) 105px;\n    gap: 10px;\n}\n\n#ft-awards-generator .ft-actions button {\n    width: 100%;\n    min-height: 52px;\n    padding: 12px 15px;\n    border-radius: 8px;\n    font: inherit;\n    font-weight: 800;\n    cursor: pointer;\n    transition:\n        background 0.2s ease,\n        border-color 0.2s ease,\n        color 0.2s ease,\n        transform 0.2s ease;\n}\n\n#ft-awards-generator .ft-actions button:hover {\n    transform: translateY(-1px);\n}\n\n#ft-awards-generator .ft-download-button {\n    border: 0;\n    background: #e30613;\n    color: #ffffff;\n}\n\n#ft-awards-generator .ft-download-button:hover {\n    background: #c7000c;\n}\n\n#ft-awards-generator .ft-download-button:disabled {\n    cursor: wait;\n    opacity: 0.58;\n    transform: none;\n}\n\n#ft-awards-generator .ft-reset-button {\n    border: 1px solid #d7d7df;\n    background: #ffffff;\n    color: #242432;\n}\n\n#ft-awards-generator .ft-reset-button:hover {\n    border-color: #232051;\n    background: #eeeef3;\n}\n\n#ft-awards-generator .ft-status {\n    min-height: 18px;\n    color: #646470;\n    font-size: 13px;\n    line-height: 1.45;\n}\n\n#ft-awards-generator .ft-status.ft-error {\n    color: #bd0010;\n}\n\n#ft-awards-generator .ft-status.ft-success {\n    color: #16844e;\n}\n\n#ft-awards-generator .ft-privacy {\n    padding: 13px 15px;\n    border-radius: 8px;\n    background: #ededf2;\n    color: #595966;\n    font-size: 12px;\n    line-height: 1.5;\n}\n\n#ft-awards-generator .ft-preview {\n    min-width: 0;\n}\n\n#ft-awards-generator .ft-preview-header {\n    display: flex;\n    justify-content: space-between;\n    gap: 15px;\n    margin-bottom: 11px;\n    color: #252532;\n    font-size: 14px;\n}\n\n#ft-awards-generator .ft-preview-header span {\n    color: #777784;\n}\n\n#ft-awards-generator canvas {\n    display: block;\n    width: 100%;\n    height: auto;\n    max-height: 900px;\n    object-fit: contain;\n    object-position: top center;\n    border-radius: 13px;\n    background: #211d51;\n    box-shadow: 0 20px 55px rgba(20, 20, 40, 0.16);\n}\n\n@media (max-width: 1020px) {\n    #ft-awards-generator .ft-generator-layout {\n        grid-template-columns: 1fr;\n    }\n\n    #ft-awards-generator .ft-preview {\n        order: 1;\n    }\n\n    #ft-awards-generator .ft-controls {\n        order: 2;\n    }\n\n    #ft-awards-generator canvas {\n        max-height: none;\n    }\n}\n\n@media (max-width: 600px) {\n    #ft-awards-generator {\n        padding: 0 13px;\n    }\n\n    #ft-awards-generator .ft-controls {\n        padding: 20px;\n    }\n\n    #ft-awards-generator .ft-field-heading {\n        align-items: flex-start;\n        flex-wrap: wrap;\n    }\n}\n\n@media (max-width: 440px) {\n    #ft-awards-generator .ft-actions {\n        grid-template-columns: 1fr;\n    }\n}\n\n\/* Prevent the preview column from overflowing its grid area *\/\n#ft-awards-generator .ft-preview {\n    min-width: 0;\n}\n\n\/*\n * Story \/ Reel:\n * preserve the 1080 \u00d7 1920 exported image,\n * but constrain the visible preview to 690px high.\n *\/\n#ft-awards-generator[data-ft-format=\"story\"] #ftAwardsCanvas {\n    display: block;\n    width: auto !important;\n    height: 690px !important;\n    max-width: 100% !important;\n    max-height: calc(100vh - 180px) !important;\n    margin-right: auto;\n    margin-left: auto;\n}\n\n\/* Square and horizontal retain their normal responsive width *\/\n#ft-awards-generator[data-ft-format=\"square\"] #ftAwardsCanvas,\n#ft-awards-generator[data-ft-format=\"horizontal\"] #ftAwardsCanvas {\n    display: block;\n    width: 100% !important;\n    height: auto !important;\n    max-height: none !important;\n    margin-right: 0;\n    margin-left: 0;\n}\n\n@media (max-width: 850px) {\n    \/*\n     * On smaller screens, use the available width rather than\n     * attempting to maintain a 690px-high desktop preview.\n     *\/\n    #ft-awards-generator[data-ft-format=\"story\"] #ftAwardsCanvas {\n        width: 100% !important;\n        height: auto !important;\n        max-height: none !important;\n    }\n}\n<\/style>\n\n<script data-wp-block-html=\"js\">\n(function () {\n  'use strict';\n\n  var VERSION = '2026-08-06-horizontal-layout-1';\n  var bootAttempts = 0;\n\n  function boot() {\n    var root = document.getElementById(\n      'ft-awards-generator'\n    );\n\n    if (!root) {\n      bootAttempts += 1;\n\n      if (bootAttempts < 80) {\n        window.setTimeout(\n          boot,\n          100\n        );\n      }\n\n      return;\n    }\n\n    if (\n      root.getAttribute(\n        'data-ft-initialized'\n      ) === '1'\n    ) {\n      return;\n    }\n\n    root.setAttribute(\n      'data-ft-initialized',\n      '1'\n    );\n\n    initialise(root);\n  }\n\n  function initialise(root) {\n    function select(selector) {\n      return root.querySelector(\n        selector\n      );\n    }\n\n    var canvas = select(\n      '#ftAwardsCanvas'\n    );\n\n    var formatInput = select(\n      '#ftVisualFormat'\n    );\n\n    var photoInput = select(\n      '#ftProfilePhoto'\n    );\n\n    var nameInput = select(\n      '#ftPersonName'\n    );\n\n    var roleInput = select(\n      '#ftPersonRole'\n    );\n\n    var companyInput = select(\n      '#ftPersonCompany'\n    );\n\n    var messageInput = select(\n      '#ftPersonMessage'\n    );\n\n    var downloadButton = select(\n      '#ftDownloadVisual'\n    );\n\n    var resetButton = select(\n      '#ftResetGenerator'\n    );\n\n    var statusBox = select(\n      '#ftGeneratorStatus'\n    );\n\n    var dimensionsBox = select(\n      '#ftPreviewDimensions'\n    );\n\n    var photoTools = select(\n      '#ftPhotoTools'\n    );\n\n    var zoomOutButton = select(\n      '#ftZoomOut'\n    );\n\n    var zoomInButton = select(\n      '#ftZoomIn'\n    );\n\n    var moveLeftButton = select(\n      '#ftMoveLeft'\n    );\n\n    var moveRightButton = select(\n      '#ftMoveRight'\n    );\n\n    var moveUpButton = select(\n      '#ftMoveUp'\n    );\n\n    var moveDownButton = select(\n      '#ftMoveDown'\n    );\n\n    var resetPhotoButton = select(\n      '#ftResetPhoto'\n    );\n\n    var styleButtons =\n      root.querySelectorAll(\n        '[data-announcement-style]'\n      );\n\n    function setStatus(\n      message,\n      type\n    ) {\n      if (!statusBox) {\n        return;\n      }\n\n      statusBox.textContent =\n        message || '';\n\n      statusBox.className =\n        'ft-status';\n\n      if (type === 'error') {\n        statusBox.className +=\n          ' ft-error';\n      }\n\n      if (type === 'success') {\n        statusBox.className +=\n          ' ft-success';\n      }\n    }\n\n    function reportError(\n      message,\n      error\n    ) {\n      setStatus(\n        message,\n        'error'\n      );\n\n      if (window.console) {\n        if (\n          typeof window.console.error ===\n          'function'\n        ) {\n          window.console.error(\n            '[FT Awards]',\n            message,\n            error || ''\n          );\n        }\n      }\n    }\n\n    var requiredElements = {\n      ftAwardsCanvas: canvas,\n      ftVisualFormat: formatInput,\n      ftProfilePhoto: photoInput,\n      ftPersonName: nameInput,\n      ftPersonRole: roleInput,\n      ftPersonCompany: companyInput,\n      ftPersonMessage: messageInput,\n      ftDownloadVisual: downloadButton,\n      ftResetGenerator: resetButton,\n      ftPreviewDimensions: dimensionsBox,\n      ftPhotoTools: photoTools,\n      ftZoomOut: zoomOutButton,\n      ftZoomIn: zoomInButton,\n      ftMoveLeft: moveLeftButton,\n      ftMoveRight: moveRightButton,\n      ftMoveUp: moveUpButton,\n      ftMoveDown: moveDownButton,\n      ftResetPhoto: resetPhotoButton\n    };\n\n    var missingElements = [];\n    var requiredName;\n\n    for (\n      requiredName in requiredElements\n    ) {\n      if (\n        !requiredElements[\n          requiredName\n        ]\n      ) {\n        missingElements.push(\n          requiredName\n        );\n      }\n    }\n\n    if (styleButtons.length === 0) {\n      missingElements.push(\n        'announcement style buttons'\n      );\n    }\n\n    if (\n      missingElements.length > 0\n    ) {\n      reportError(\n        'Missing required elements: ' +\n        missingElements.join(', ')\n      );\n\n      return;\n    }\n\n    var context =\n      canvas.getContext('2d');\n\n    if (!context) {\n      reportError(\n        'Canvas 2D is not available in this browser.'\n      );\n\n      return;\n    }\n\n    var templates = {\n      square: {\n        width: 1200,\n        height: 1200,\n\n        background:\n          'https:\/\/www.frenchtechberlin.com\/' +\n          'wp-content\/uploads\/2026\/08\/' +\n          'french-tech-awards-2026-custom-square.png',\n\n        photo: {\n          x: 735,\n          y: 185,\n          width: 350,\n          height: 420,\n          radius: 30\n        },\n\n        announcement: {\n          meetAt: {\n            centerX: 315,\n            centerY: 125,\n            minimumWidth: 210,\n            maximumWidth: 520,\n            height: 80,\n            fontSize: 50,\n            minimumFontSize: 19\n          },\n\n          standard: {\n            centerX: 320,\n            centerY: 750,\n            minimumWidth: 210,\n            maximumWidth: 560,\n            height: 80,\n            fontSize: 50,\n            minimumFontSize: 20\n          }\n        },\n\n        name: {\n          x: 910,\n          y: 670,\n          maximumWidth: 465,\n          startingSize: 47,\n          minimumSize: 24,\n          align: 'center'\n        },\n\n        role: {\n          x: 910,\n          y: 730,\n          maximumWidth: 465,\n          startingSize: 40,\n          minimumSize: 20,\n          align: 'center'\n        },\n\n        company: {\n          x: 910,\n          y: 785,\n          maximumWidth: 465,\n          startingSize: 40,\n          minimumSize: 20,\n          align: 'center'\n        }\n      },\n\n      horizontal: {\n        width: 2160,\n        height: 1080,\n\n        background:\n          'https:\/\/www.frenchtechberlin.com\/' +\n          'wp-content\/uploads\/2026\/08\/' +\n          'french-tech-awards-2026-custom-horizontal.png',\n\n        \/*\n         * The photo is lower than in the previous\n         * horizontal layout.\n         *\/\n        photo: {\n          x: 1135,\n          y: 245,\n          width: 410,\n          height: 470,\n          radius: 34\n        },\n\n        \/*\n         * In horizontal format, every announcement\n         * is positioned beneath the event title.\n         *\/\n        announcement: {\n          meetAt: {\n            centerX: 430,\n            centerY: 150,\n            minimumWidth: 260,\n            maximumWidth: 720,\n            height: 84,\n            fontSize: 43,\n            minimumFontSize: 23\n          },\n\n          standard: {\n            centerX: 430,\n            centerY: 790,\n            minimumWidth: 260,\n            maximumWidth: 720,\n            height: 84,\n            fontSize: 43,\n            minimumFontSize: 23\n          }\n        },\n\n        \/*\n         * Identity text is placed to the right of\n         * the photo and aligned left.\n         *\/\n        name: {\n          x: 1590,\n          y: 410,\n          maximumWidth: 500,\n          startingSize: 52,\n          minimumSize: 27,\n          align: 'left'\n        },\n\n        role: {\n          x: 1590,\n          y: 480,\n          maximumWidth: 500,\n          startingSize: 44,\n          minimumSize: 25,\n          align: 'left'\n        },\n\n        company: {\n          x: 1590,\n          y: 530,\n          maximumWidth: 500,\n          startingSize: 38,\n          minimumSize: 20,\n          align: 'left'\n        }\n      },\n\n      story: {\n        width: 1080,\n        height: 1920,\n\n        background:\n          'https:\/\/www.frenchtechberlin.com\/' +\n          'wp-content\/uploads\/2026\/08\/' +\n          'french-tech-awards-2026-custom-story.png',\n\n        photo: {\n          x: 300,\n          y: 820,\n          width: 480,\n          height: 560,\n          radius: 38\n        },\n\n        announcement: {\n          meetAt: {\n            centerX: 355,\n            centerY: 205,\n            minimumWidth: 220,\n            maximumWidth: 590,\n            height: 76,\n            fontSize: 36,\n            minimumFontSize: 20\n          },\n\n          standard: {\n            centerX: 540,\n            centerY: 720,\n            minimumWidth: 230,\n            maximumWidth: 680,\n            height: 82,\n            fontSize: 39,\n            minimumFontSize: 21\n          }\n        },\n\n        name: {\n          x: 540,\n          y: 1445,\n          maximumWidth: 760,\n          startingSize: 55,\n          minimumSize: 28,\n          align: 'center'\n        },\n\n        role: {\n          x: 540,\n          y: 1515,\n          maximumWidth: 760,\n          startingSize: 42,\n          minimumSize: 22,\n          align: 'center'\n        },\n\n        company: {\n          x: 540,\n          y: 1575,\n          maximumWidth: 760,\n          startingSize: 40,\n          minimumSize: 21,\n          align: 'center'\n        }\n      }\n    };\n\n    var backgrounds = {};\n    var currentBackground = null;\n    var profileImage = null;\n    var backgroundIsLoading = false;\n\n    \/*\n     * White Card is the default announcement style.\n     *\/\n    var announcementStyle =\n      'card';\n\n    var photoTransform = {\n      zoom: 1,\n      x: 0,\n      y: 0\n    };\n\n    function clamp(\n      value,\n      minimum,\n      maximum\n    ) {\n      return Math.min(\n        maximum,\n        Math.max(\n          minimum,\n          value\n        )\n      );\n    }\n\n    function trimValue(value) {\n      return String(\n        value || ''\n      ).replace(\n        \/^\\s+|\\s+$\/g,\n        ''\n      );\n    }\n\n    function getTemplate() {\n      var format =\n        formatInput.value;\n\n      if (!templates[format]) {\n        format = 'square';\n      }\n\n      return templates[format];\n    }\n\n    function getAnnouncementArea(\n      template,\n      message\n    ) {\n      var messageValue =\n        trimValue(message)\n        .toLowerCase();\n\n      if (\n        messageValue ===\n        'meet me at'\n      ) {\n        return template\n          .announcement\n          .meetAt;\n      }\n\n      return template\n        .announcement\n        .standard;\n    }\n\n    function resetPhotoTransform() {\n      photoTransform.zoom = 1;\n      photoTransform.x = 0;\n      photoTransform.y = 0;\n\n      updatePhotoToolState();\n    }\n\n    function updatePhotoToolState() {\n      zoomOutButton.disabled =\n        photoTransform.zoom <= 1;\n\n      zoomInButton.disabled =\n        photoTransform.zoom >= 3;\n    }\n\n    function showPhotoTools() {\n      photoTools.hidden = false;\n      updatePhotoToolState();\n    }\n\n    function hidePhotoTools() {\n      photoTools.hidden = true;\n    }\n\n    function setAnnouncementStyle(\n      style\n    ) {\n      var allowedStyles = {\n        editorial: true,\n        classic: true,\n        outline: true,\n        card: true\n      };\n\n      if (!allowedStyles[style]) {\n        style = 'card';\n      }\n\n      announcementStyle = style;\n\n      var styleIndex;\n\n      for (\n        styleIndex = 0;\n        styleIndex <\n        styleButtons.length;\n        styleIndex += 1\n      ) {\n        var styleButton =\n          styleButtons[\n            styleIndex\n          ];\n\n        var isActive =\n          styleButton.getAttribute(\n            'data-announcement-style'\n          ) === style;\n\n        styleButton.classList.toggle(\n          'is-active',\n          isActive\n        );\n\n        styleButton.setAttribute(\n          'aria-pressed',\n          isActive ?\n            'true' :\n            'false'\n        );\n      }\n\n      draw();\n    }\n\n    function roundedPath(\n      x,\n      y,\n      width,\n      height,\n      radius\n    ) {\n      var finalRadius =\n        Math.min(\n          radius,\n          width \/ 2,\n          height \/ 2\n        );\n\n      context.beginPath();\n\n      context.moveTo(\n        x + finalRadius,\n        y\n      );\n\n      context.lineTo(\n        x +\n        width -\n        finalRadius,\n        y\n      );\n\n      context.quadraticCurveTo(\n        x + width,\n        y,\n        x + width,\n        y + finalRadius\n      );\n\n      context.lineTo(\n        x + width,\n        y +\n        height -\n        finalRadius\n      );\n\n      context.quadraticCurveTo(\n        x + width,\n        y + height,\n        x +\n        width -\n        finalRadius,\n        y + height\n      );\n\n      context.lineTo(\n        x + finalRadius,\n        y + height\n      );\n\n      context.quadraticCurveTo(\n        x,\n        y + height,\n        x,\n        y +\n        height -\n        finalRadius\n      );\n\n      context.lineTo(\n        x,\n        y + finalRadius\n      );\n\n      context.quadraticCurveTo(\n        x,\n        y,\n        x + finalRadius,\n        y\n      );\n\n      context.closePath();\n    }\n\n    function drawImageCover(\n      image,\n      area\n    ) {\n      var imageWidth =\n        image.naturalWidth ||\n        image.width;\n\n      var imageHeight =\n        image.naturalHeight ||\n        image.height;\n\n      var baseScale =\n        Math.max(\n          area.width \/\n          imageWidth,\n\n          area.height \/\n          imageHeight\n        );\n\n      var finalScale =\n        baseScale *\n        photoTransform.zoom;\n\n      var drawWidth =\n        imageWidth *\n        finalScale;\n\n      var drawHeight =\n        imageHeight *\n        finalScale;\n\n      var overflowX =\n        Math.max(\n          0,\n          drawWidth -\n          area.width\n        );\n\n      var overflowY =\n        Math.max(\n          0,\n          drawHeight -\n          area.height\n        );\n\n      var drawX =\n        area.x +\n        (\n          area.width -\n          drawWidth\n        ) \/\n        2 +\n        photoTransform.x *\n        overflowX \/\n        2;\n\n      var drawY =\n        area.y +\n        (\n          area.height -\n          drawHeight\n        ) \/\n        2 +\n        photoTransform.y *\n        overflowY \/\n        2;\n\n      drawX = clamp(\n        drawX,\n        area.x +\n        area.width -\n        drawWidth,\n        area.x\n      );\n\n      drawY = clamp(\n        drawY,\n        area.y +\n        area.height -\n        drawHeight,\n        area.y\n      );\n\n      context.drawImage(\n        image,\n        drawX,\n        drawY,\n        drawWidth,\n        drawHeight\n      );\n    }\n\n    function drawProfile(\n      image,\n      area\n    ) {\n      context.save();\n\n      roundedPath(\n        area.x,\n        area.y,\n        area.width,\n        area.height,\n        area.radius\n      );\n\n      context.clip();\n\n      drawImageCover(\n        image,\n        area\n      );\n\n      context.restore();\n\n      context.save();\n\n      roundedPath(\n        area.x,\n        area.y,\n        area.width,\n        area.height,\n        area.radius\n      );\n\n      context.strokeStyle =\n        '#ffffff';\n\n      context.lineWidth = 9;\n      context.stroke();\n\n      context.restore();\n    }\n\n    function fitText(\n      text,\n      maximumWidth,\n      startingSize,\n      minimumSize,\n      weight,\n      family\n    ) {\n      var size =\n        startingSize;\n\n      while (\n        size >\n        minimumSize\n      ) {\n        context.font =\n          weight +\n          ' ' +\n          size +\n          'px ' +\n          family;\n\n        if (\n          context\n          .measureText(text)\n          .width <=\n          maximumWidth\n        ) {\n          break;\n        }\n\n        size -= 1;\n      }\n\n      return size;\n    }\n\n    function drawPhotoPlaceholder(\n      area\n    ) {\n      var title =\n        'ADD YOUR PHOTO';\n\n      var subtitle =\n        'Your portrait will appear here';\n\n      var titleSize =\n        fitText(\n          title,\n          area.width - 54,\n          Math.max(\n            32,\n            area.width * 0.105\n          ),\n          19,\n          800,\n          'Arial'\n        );\n\n      var subtitleSize =\n        fitText(\n          subtitle,\n          area.width - 50,\n          Math.max(\n            18,\n            area.width * 0.052\n          ),\n          13,\n          500,\n          'Arial'\n        );\n\n      context.save();\n\n      roundedPath(\n        area.x,\n        area.y,\n        area.width,\n        area.height,\n        area.radius\n      );\n\n      context.fillStyle =\n        'rgba(255,255,255,0.50)';\n\n      context.fill();\n\n      context.strokeStyle =\n        'rgba(255,255,255,0.82)';\n\n      context.lineWidth = 5;\n      context.stroke();\n\n      context.textAlign =\n        'center';\n\n      context.textBaseline =\n        'middle';\n\n      context.fillStyle =\n        '#4a4a56';\n\n      context.font =\n        '800 ' +\n        titleSize +\n        'px Arial';\n\n      context.fillText(\n        title,\n        area.x +\n        area.width \/ 2,\n        area.y +\n        area.height \/ 2 -\n        titleSize * 0.48\n      );\n\n      context.fillStyle =\n        '#5d5d68';\n\n      context.font =\n        '500 ' +\n        subtitleSize +\n        'px Arial';\n\n      context.fillText(\n        subtitle,\n        area.x +\n        area.width \/ 2,\n        area.y +\n        area.height \/ 2 +\n        subtitleSize * 1.25\n      );\n\n      context.restore();\n    }\n\n    \/*\n     * Despite its historical function name, this\n     * now supports both centred and left-aligned text.\n     *\/\n    function drawCenteredText(\n      text,\n      settings,\n      weight,\n      color\n    ) {\n      var value =\n        String(\n          text || ''\n        ).toUpperCase();\n\n      var fontSize =\n        fitText(\n          value,\n          settings.maximumWidth,\n          settings.startingSize,\n          settings.minimumSize,\n          weight,\n          'Arial'\n        );\n\n      var alignment =\n        settings.align ||\n        'center';\n\n      context.save();\n\n      context.font =\n        weight +\n        ' ' +\n        fontSize +\n        'px Arial';\n\n      context.fillStyle =\n        color;\n\n      context.textAlign =\n        alignment;\n\n      context.textBaseline =\n        'alphabetic';\n\n      context.shadowColor =\n        'rgba(0,0,0,0.36)';\n\n      context.shadowBlur = 8;\n      context.shadowOffsetY = 3;\n\n      context.fillText(\n        value,\n        settings.x,\n        settings.y\n      );\n\n      context.restore();\n    }\n\n    function getDynamicBox(\n      text,\n      area,\n      padding,\n      weight,\n      family\n    ) {\n      var fontSize =\n        fitText(\n          text,\n          area.maximumWidth -\n          padding * 2,\n          area.fontSize,\n          area.minimumFontSize,\n          weight,\n          family\n        );\n\n      context.font =\n        weight +\n        ' ' +\n        fontSize +\n        'px ' +\n        family;\n\n      var measuredWidth =\n        context\n        .measureText(text)\n        .width;\n\n      var width =\n        clamp(\n          measuredWidth +\n          padding * 2,\n          area.minimumWidth,\n          area.maximumWidth\n        );\n\n      return {\n        x:\n          area.centerX -\n          width \/ 2,\n\n        y:\n          area.centerY -\n          area.height \/ 2,\n\n        width: width,\n        height: area.height,\n        fontSize: fontSize\n      };\n    }\n\n    function drawEditorial(\n      text,\n      area\n    ) {\n      var fontSize =\n        fitText(\n          text,\n          area.maximumWidth,\n          area.fontSize + 3,\n          area.minimumFontSize,\n          900,\n          'Arial'\n        );\n\n      context.save();\n\n      context.font =\n        '900 ' +\n        fontSize +\n        'px Arial';\n\n      context.textAlign =\n        'center';\n\n      context.textBaseline =\n        'middle';\n\n      context.fillStyle =\n        '#ffffff';\n\n      context.shadowColor =\n        'rgba(0,0,0,0.48)';\n\n      context.shadowBlur = 9;\n      context.shadowOffsetY = 3;\n\n      context.fillText(\n        text,\n        area.centerX,\n        area.centerY - 5\n      );\n\n      var textWidth =\n        context\n        .measureText(text)\n        .width;\n\n      var underlineWidth =\n        Math.min(\n          textWidth,\n          area.maximumWidth\n        );\n\n      context.shadowColor =\n        'transparent';\n\n      context.fillStyle =\n        '#e0bd27';\n\n      context.fillRect(\n        area.centerX -\n        underlineWidth \/ 2,\n\n        area.centerY +\n        fontSize * 0.55,\n\n        underlineWidth,\n\n        Math.max(\n          4,\n          fontSize * 0.09\n        )\n      );\n\n      context.restore();\n    }\n\n    function drawClassic(\n      text,\n      area\n    ) {\n      var box =\n        getDynamicBox(\n          text,\n          area,\n          30,\n          900,\n          'Arial'\n        );\n\n      context.save();\n\n      context.shadowColor =\n        'rgba(0,0,0,0.30)';\n\n      context.shadowBlur = 12;\n      context.shadowOffsetY = 5;\n\n      roundedPath(\n        box.x,\n        box.y,\n        box.width,\n        box.height,\n        11\n      );\n\n      context.fillStyle =\n        '#e30613';\n\n      context.fill();\n      context.restore();\n\n      context.save();\n\n      context.font =\n        '900 ' +\n        box.fontSize +\n        'px Arial';\n\n      context.textAlign =\n        'center';\n\n      context.textBaseline =\n        'middle';\n\n      context.fillStyle =\n        '#ffffff';\n\n      context.fillText(\n        text,\n        area.centerX,\n        area.centerY + 1\n      );\n\n      context.restore();\n    }\n\n    function drawOutline(\n      text,\n      area\n    ) {\n      var box =\n        getDynamicBox(\n          text,\n          area,\n          30,\n          900,\n          'Arial'\n        );\n\n      context.save();\n\n      roundedPath(\n        box.x,\n        box.y,\n        box.width,\n        box.height,\n        11\n      );\n\n      context.fillStyle =\n        'rgba(18,16,62,0.20)';\n\n      context.fill();\n\n      context.strokeStyle =\n        '#ffffff';\n\n      context.lineWidth = 4;\n      context.stroke();\n\n      context.font =\n        '900 ' +\n        box.fontSize +\n        'px Arial';\n\n      context.textAlign =\n        'center';\n\n      context.textBaseline =\n        'middle';\n\n      context.fillStyle =\n        '#ffffff';\n\n      context.shadowColor =\n        'rgba(0,0,0,0.32)';\n\n      context.shadowBlur = 7;\n      context.shadowOffsetY = 2;\n\n      context.fillText(\n        text,\n        area.centerX,\n        area.centerY + 1\n      );\n\n      context.restore();\n    }\n\n    function drawCard(\n      text,\n      area\n    ) {\n      var box =\n        getDynamicBox(\n          text,\n          area,\n          34,\n          900,\n          '\"Trebuchet MS\"'\n        );\n\n      context.save();\n\n      context.translate(\n        area.centerX,\n        area.centerY\n      );\n\n      context.rotate(\n        -0.028\n      );\n\n      context.shadowColor =\n        'rgba(0,0,0,0.28)';\n\n      context.shadowBlur = 14;\n      context.shadowOffsetY = 6;\n\n      roundedPath(\n        -box.width \/ 2,\n        -box.height \/ 2,\n        box.width,\n        box.height,\n        8\n      );\n\n      context.fillStyle =\n        '#ffffff';\n\n      context.fill();\n\n      context.shadowColor =\n        'transparent';\n\n      context.fillStyle =\n        '#e30613';\n\n      context.fillRect(\n        -box.width \/ 2,\n        -box.height \/ 2,\n        8,\n        box.height\n      );\n\n      context.font =\n        '900 ' +\n        box.fontSize +\n        'px \"Trebuchet MS\"';\n\n      context.textAlign =\n        'center';\n\n      context.textBaseline =\n        'middle';\n\n      context.fillStyle =\n        '#211d51';\n\n      context.fillText(\n        text,\n        4,\n        1\n      );\n\n      context.restore();\n    }\n\n    function drawAnnouncement(\n      message,\n      template\n    ) {\n      var text =\n        String(\n          message ||\n          'I will be there'\n        ).toUpperCase();\n\n      var area =\n        getAnnouncementArea(\n          template,\n          message\n        );\n\n      if (\n        announcementStyle ===\n        'editorial'\n      ) {\n        drawEditorial(\n          text,\n          area\n        );\n\n        return;\n      }\n\n      if (\n        announcementStyle ===\n        'outline'\n      ) {\n        drawOutline(\n          text,\n          area\n        );\n\n        return;\n      }\n\n      if (\n        announcementStyle ===\n        'card'\n      ) {\n        drawCard(\n          text,\n          area\n        );\n\n        return;\n      }\n\n      drawClassic(\n        text,\n        area\n      );\n    }\n\n    function hasLoadedBackground() {\n      if (!currentBackground) {\n        return false;\n      }\n\n      if (\n        !currentBackground.complete\n      ) {\n        return false;\n      }\n\n      if (\n        currentBackground\n        .naturalWidth <= 0\n      ) {\n        return false;\n      }\n\n      return true;\n    }\n\n    function slugify(value) {\n      var result =\n        trimValue(value)\n        .toLowerCase();\n\n      if (\n        typeof result.normalize ===\n        'function'\n      ) {\n        result =\n          result\n          .normalize('NFD')\n          .replace(\n            \/[\\u0300-\\u036f]\/g,\n            ''\n          );\n      }\n\n      result =\n        result\n        .replace(\n          \/[^a-z0-9]+\/g,\n          '-'\n        )\n        .replace(\n          \/^-+|-+$\/g,\n          ''\n        );\n\n      return result ||\n        'participant';\n    }\n\n    function draw() {\n      try {\n        var template =\n          getTemplate();\n\n\/*\n     * Used by the CSS to apply a different preview size\n     * without changing the downloaded canvas dimensions.\n     *\/\n    root.setAttribute(\n      'data-ft-format',\n      formatInput.value || 'square'\n    );\n\n        var name =\n          trimValue(\n            nameInput.value\n          ) ||\n          'FIRSTNAME LASTNAME';\n\n        var role =\n          trimValue(\n            roleInput.value\n          ) ||\n          'ROLE';\n\n        var company =\n          trimValue(\n            companyInput.value\n          ) ||\n          'COMPANY';\n\n        var message =\n          messageInput.value ||\n          'I will be there';\n\n        canvas.width =\n          template.width;\n\n        canvas.height =\n          template.height;\n\n        dimensionsBox.textContent =\n          template.width +\n          ' \u00d7 ' +\n          template.height;\n\n        context.clearRect(\n          0,\n          0,\n          canvas.width,\n          canvas.height\n        );\n\n        if (\n          hasLoadedBackground()\n        ) {\n          context.drawImage(\n            currentBackground,\n            0,\n            0,\n            canvas.width,\n            canvas.height\n          );\n        } else {\n          context.fillStyle =\n            '#211d51';\n\n          context.fillRect(\n            0,\n            0,\n            canvas.width,\n            canvas.height\n          );\n        }\n\n        if (profileImage) {\n          drawProfile(\n            profileImage,\n            template.photo\n          );\n        } else {\n          drawPhotoPlaceholder(\n            template.photo\n          );\n        }\n\n        drawAnnouncement(\n          message,\n          template\n        );\n\n        drawCenteredText(\n          name,\n          template.name,\n          900,\n          '#ffffff'\n        );\n\n        drawCenteredText(\n          role,\n          template.role,\n          750,\n          '#f1c920'\n        );\n\n        drawCenteredText(\n          company,\n          template.company,\n          750,\n          '#f1c920'\n        );\n      } catch (error) {\n        reportError(\n          'The preview could not be drawn.',\n          error\n        );\n      }\n    }\n\n    function loadBackground() {\n      try {\n        var format =\n          formatInput.value;\n\n        var template =\n          getTemplate();\n\n        if (\n          backgrounds[format]\n        ) {\n          currentBackground =\n            backgrounds[format];\n\n          backgroundIsLoading =\n            false;\n\n          downloadButton.disabled =\n            false;\n\n          setStatus('', '');\n          draw();\n\n          return;\n        }\n\n        backgroundIsLoading =\n          true;\n\n        downloadButton.disabled =\n          true;\n\n        currentBackground =\n          null;\n\n        setStatus(\n          'Loading background...',\n          ''\n        );\n\n        draw();\n\n        var image =\n          new Image();\n\n        image.onload =\n          function () {\n            backgrounds[format] =\n              image;\n\n            currentBackground =\n              image;\n\n            backgroundIsLoading =\n              false;\n\n            downloadButton.disabled =\n              false;\n\n            setStatus('', '');\n            draw();\n          };\n\n        image.onerror =\n          function () {\n            currentBackground =\n              null;\n\n            backgroundIsLoading =\n              false;\n\n            downloadButton.disabled =\n              false;\n\n            reportError(\n              'The background image could not be loaded. Check the media URL.'\n            );\n\n            draw();\n          };\n\n        image.src =\n          template.background +\n          '?v=' +\n          VERSION;\n      } catch (error) {\n        reportError(\n          'Background loading failed.',\n          error\n        );\n      }\n    }\n\n    function adjustPhotoZoom(\n      amount\n    ) {\n      photoTransform.zoom =\n        clamp(\n          photoTransform.zoom +\n          amount,\n          1,\n          3\n        );\n\n      updatePhotoToolState();\n      draw();\n    }\n\n    function adjustPhotoPosition(\n      horizontalAmount,\n      verticalAmount\n    ) {\n      photoTransform.x =\n        clamp(\n          photoTransform.x +\n          horizontalAmount,\n          -1,\n          1\n        );\n\n      photoTransform.y =\n        clamp(\n          photoTransform.y +\n          verticalAmount,\n          -1,\n          1\n        );\n\n      draw();\n    }\n\n    photoInput.addEventListener(\n      'change',\n      function () {\n        try {\n          var file = null;\n\n          if (\n            photoInput.files\n          ) {\n            if (\n              photoInput.files\n              .length > 0\n            ) {\n              file =\n                photoInput\n                .files[0];\n            }\n          }\n\n          if (!file) {\n            profileImage = null;\n\n            resetPhotoTransform();\n            hidePhotoTools();\n\n            setStatus('', '');\n            draw();\n\n            return;\n          }\n\n          if (\n            file.type.indexOf(\n              'image\/'\n            ) !== 0\n          ) {\n            setStatus(\n              'Please choose a valid image file.',\n              'error'\n            );\n\n            return;\n          }\n\n          if (\n            file.size >\n            10 *\n            1024 *\n            1024\n          ) {\n            setStatus(\n              'The image is larger than 10 MB.',\n              'error'\n            );\n\n            return;\n          }\n\n          setStatus(\n            'Loading your photo...',\n            ''\n          );\n\n          var reader =\n            new FileReader();\n\n          reader.onload =\n            function (event) {\n              var uploadedImage =\n                new Image();\n\n              uploadedImage.onload =\n                function () {\n                  profileImage =\n                    uploadedImage;\n\n                  resetPhotoTransform();\n                  showPhotoTools();\n\n                  setStatus(\n                    'Photo loaded. Use the controls to adjust it.',\n                    'success'\n                  );\n\n                  draw();\n                };\n\n              uploadedImage.onerror =\n                function () {\n                  reportError(\n                    'The selected photo could not be loaded.'\n                  );\n                };\n\n              uploadedImage.src =\n                event.target.result;\n            };\n\n          reader.onerror =\n            function () {\n              reportError(\n                'The selected photo could not be read.'\n              );\n            };\n\n          reader.readAsDataURL(\n            file\n          );\n        } catch (error) {\n          reportError(\n            'Photo processing failed.',\n            error\n          );\n        }\n      }\n    );\n\n    zoomOutButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoZoom(\n          -0.1\n        );\n      }\n    );\n\n    zoomInButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoZoom(\n          0.1\n        );\n      }\n    );\n\n    moveLeftButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoPosition(\n          -0.16,\n          0\n        );\n      }\n    );\n\n    moveRightButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoPosition(\n          0.16,\n          0\n        );\n      }\n    );\n\n    moveUpButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoPosition(\n          0,\n          -0.16\n        );\n      }\n    );\n\n    moveDownButton.addEventListener(\n      'click',\n      function () {\n        adjustPhotoPosition(\n          0,\n          0.16\n        );\n      }\n    );\n\n    resetPhotoButton.addEventListener(\n      'click',\n      function () {\n        resetPhotoTransform();\n        draw();\n\n        setStatus(\n          'Photo zoom and position reset.',\n          'success'\n        );\n      }\n    );\n\n    var styleIndex;\n\n    for (\n      styleIndex = 0;\n      styleIndex <\n      styleButtons.length;\n      styleIndex += 1\n    ) {\n      styleButtons[\n        styleIndex\n      ].addEventListener(\n        'click',\n        function () {\n          setAnnouncementStyle(\n            this.getAttribute(\n              'data-announcement-style'\n            )\n          );\n        }\n      );\n    }\n\n    formatInput.addEventListener(\n      'change',\n      loadBackground\n    );\n\n    nameInput.addEventListener(\n      'input',\n      draw\n    );\n\n    roleInput.addEventListener(\n      'input',\n      draw\n    );\n\n    companyInput.addEventListener(\n      'input',\n      draw\n    );\n\n    messageInput.addEventListener(\n      'change',\n      draw\n    );\n\n    resetButton.addEventListener(\n      'click',\n      function () {\n        formatInput.value =\n          'square';\n\n        photoInput.value =\n          '';\n\n        nameInput.value =\n          '';\n\n        roleInput.value =\n          '';\n\n        companyInput.value =\n          '';\n\n        messageInput.value =\n          'I will be there';\n\n        profileImage =\n          null;\n\n        resetPhotoTransform();\n        hidePhotoTools();\n\n        setAnnouncementStyle(\n          'card'\n        );\n\n        setStatus(\n          'The visual has been reset.',\n          'success'\n        );\n\n        loadBackground();\n      }\n    );\n\n    downloadButton.addEventListener(\n      'click',\n      function () {\n        try {\n          if (\n            backgroundIsLoading\n          ) {\n            setStatus(\n              'Please wait until the background has loaded.',\n              'error'\n            );\n\n            return;\n          }\n\n          draw();\n\n          var filename =\n            'french-tech-awards-2026-' +\n            formatInput.value +\n            '-' +\n            slugify(\n              nameInput.value\n            ) +\n            '.png';\n\n          var link =\n            document.createElement(\n              'a'\n            );\n\n          link.download =\n            filename;\n\n          link.href =\n            canvas.toDataURL(\n              'image\/png'\n            );\n\n          document.body\n            .appendChild(\n              link\n            );\n\n          link.click();\n\n          document.body\n            .removeChild(\n              link\n            );\n\n          setStatus(\n            'Downloaded as ' +\n            filename,\n            'success'\n          );\n        } catch (error) {\n          reportError(\n            'The PNG could not be generated.',\n            error\n          );\n        }\n      }\n    );\n\n    hidePhotoTools();\n    updatePhotoToolState();\n\n    setAnnouncementStyle(\n      'card'\n    );\n\n    loadBackground();\n  }\n\n  if (\n    document.readyState ===\n    'loading'\n  ) {\n    document.addEventListener(\n      'DOMContentLoaded',\n      boot\n    );\n  } else {\n    boot();\n  }\n\n  window.addEventListener(\n    'load',\n    boot\n  );\n\n  window.setTimeout(\n    boot,\n    0\n  );\n})();\n<\/script>\n\n<div id=\"ft-awards-generator\">\n\n    <div class=\"ft-generator-header\">\n        <span class=\"ft-generator-eyebrow\">\n  Prix \u00ab La French Tech en Allemagne \u00bb 2026\n        <\/span>\n\n        <h2>Personnalisez votre visuel de participation<\/h2>\n\n        <p>\n  Ajoutez votre photo et vos informations, choisissez un format, puis t\u00e9l\u00e9chargez votre visuel personnalis\u00e9 \u00ab\n \u00bb.\n        <\/p>\n    <\/div>\n\n    <div class=\"ft-generator-layout\">\n\n        <div class=\"ft-controls\">\n\n            <div class=\"ft-field\">\n                <label for=\"ftVisualFormat\">\n  1. Choisissez un format\n                <\/label>\n\n                <select id=\"ftVisualFormat\">\n                     <option value=\"square\">\n Carr\u00e9 \u2014 1 200 \u00d7 1 200\n                   <\/option>\n\n                    <option value=\"horizontal\">\n Horizontal \u2014 2160 \u00d7 1080\n                   <\/option>\n\n                    <option value=\"story\">\n Story \/ Reel \u2014 1 080 \u00d7 1 920\n                   <\/option> \n                <\/select>\n            <\/div>\n\n            <div class=\"ft-field\">\n\n                <div class=\"ft-field-heading\">\n                    <label for=\"ftProfilePhoto\">\n  2. Photo de profil\n                    <\/label>\n\n                    <div id=\"ftPhotoTools\" class=\"ft-photo-tools\" aria-label=\"Photo positioning controls\" hidden=\"\">\n                        <button type=\"button\" id=\"ftZoomOut\" aria-label=\"Zoom out\" title=\"R\u00e9duire le zoom\">\u2212<\/button>\n\n                        <button type=\"button\" id=\"ftZoomIn\" aria-label=\"Zoom in\" title=\"Agrandir\">+<\/button>\n\n                        <span class=\"ft-tool-divider\"><\/span>\n\n                        <button type=\"button\" id=\"ftMoveLeft\" aria-label=\"Move photo left\" title=\"D\u00e9placer la photo vers la gauche\">\u2190<\/button>\n\n                        <button type=\"button\" id=\"ftMoveRight\" aria-label=\"Move photo right\" title=\"D\u00e9placer la photo vers la droite\">\u2192<\/button>\n\n                        <button type=\"button\" id=\"ftMoveUp\" aria-label=\"Move photo up\" title=\"D\u00e9placer la photo vers le haut\">\u2191<\/button>\n\n                        <button type=\"button\" id=\"ftMoveDown\" aria-label=\"Move photo down\" title=\"D\u00e9placer la photo vers le bas\">\u2193<\/button>\n\n                        <button type=\"button\" id=\"ftResetPhoto\" aria-label=\"Reset photo position\" title=\"R\u00e9initialiser le zoom et la position\">\u21ba<\/button>\n                    <\/div>\n                <\/div>\n\n                <input type=\"file\" id=\"ftProfilePhoto\" accept=\"image\/jpeg,image\/png,image\/webp,image\/gif\"\/>\n\n                <small>\n  JPG, PNG, WebP ou GIF. Taille maximale du fichier : 10 Mo. \n                <\/small>\n            <\/div>\n\n            <div class=\"ft-field\">\n                <label for=\"ftPersonName\">\n  3. Nom\n                <\/label>\n\n                <input type=\"text\" id=\"ftPersonName\" placeholder=\"Pr\u00e9nom Nom\" maxlength=\"55\"\/>\n            <\/div>\n\n            <div class=\"ft-field\">\n                <label for=\"ftPersonRole\">\n  4. Fonction\n                <\/label>\n\n                <input type=\"text\" id=\"ftPersonRole\" placeholder=\"Fonction\" maxlength=\"60\"\/>\n            <\/div>\n\n            <div class=\"ft-field\">\n                <label for=\"ftPersonCompany\">\n  5. Entreprise\n                <\/label>\n\n                <input type=\"text\" id=\"ftPersonCompany\" placeholder=\"Entreprise\" maxlength=\"70\"\/>\n            <\/div>\n\n            <div class=\"ft-field\">\n\n                <div class=\"ft-field-heading\">\n                    <label for=\"ftPersonMessage\">\n  6. Message\n                    <\/label>\n\n                    <div id=\"ftAnnouncementStyles\" class=\"ft-announcement-styles\" role=\"group\" aria-label=\"Announcement design\">\n                        <button type=\"button\" data-announcement-style=\"editorial\" aria-label=\"Style 1: Editorial text\" title=\"Style n\u00b0 1 : \u00c9ditorial\">\n                            <span class=\"ft-style-preview ft-style-editorial\">\n  Aa\n                            <\/span>\n                        <\/button>\n\n                        <button type=\"button\" data-announcement-style=\"classic\" aria-label=\"Style 2: Classic red\" title=\"Mod\u00e8le 2 : Rouge classique\" aria-pressed=\"true\" class=\"is-active\">\n                            <span class=\"ft-style-preview ft-style-classic\">\n  A\n                            <\/span>\n                        <\/button>\n\n                        <button type=\"button\" data-announcement-style=\"outline\" aria-label=\"Style 3: White outline\" title=\"Style 3 : Contour blanc\">\n                            <span class=\"ft-style-preview ft-style-outline\">\n  A\n                            <\/span>\n                        <\/button>\n\n                        <button type=\"button\" data-announcement-style=\"card\" aria-label=\"Style 4: White card\" title=\"Mod\u00e8le n\u00b0 4 : Carte blanche\">\n                            <span class=\"ft-style-preview ft-style-card\">\n  A\n                            <\/span>\n                        <\/button>\n\n                         \n                    <\/div>\n                <\/div>\n\n                <select id=\"ftPersonMessage\">\n                    <option value=\"I am applying\">\n  Je postule\n                    <\/option>\n\n                    <option value=\"I will be there\" selected=\"selected\">\n  J'y serai\n                    <\/option>\n\n                    <option value=\"Meet me at\">\n  Retrouvez-moi \u00e0\n                    <\/option>\n\n                    <option value=\"Meet me there\">\n  Retrouvez-moi l\u00e0-bas\n                    <\/option>\n\n                    <option value=\"I am a jury member\">\n  Je suis membre du jury\n                    <\/option>\n\n                    <option value=\"We are partners\">\n  Nous sommes partenaires\n                    <\/option>\n\n                    <option value=\"We are finalists\">\n  Nous sommes finalistes\n                    <\/option>\n\n                    <option value=\"We are nominees\">\n  Nous sommes en lice\n                    <\/option>\n\n                <\/select>\n            <\/div>\n\n            <div class=\"ft-actions\">\n                 <button type=\"button\" id=\"ftDownloadVisual\" class=\"ft-download-button\">\n T\u00e9l\u00e9charger le fichier PNG \u00ab\n \u00bb                <\/button>\n\n                <button type=\"button\" id=\"ftResetGenerator\" class=\"ft-reset-button\">\n R\u00e9initialiser\n               <\/button> \n            <\/div>\n\n            <div id=\"ftGeneratorStatus\" class=\"ft-status\" aria-live=\"polite\"><\/div>\n\n            <div class=\"ft-privacy\">\n  Votre photo reste dans votre navigateur et n'est pas t\u00e9l\u00e9charg\u00e9e sur notre serveur\n.\n            <\/div>\n\n        <\/div>\n\n        <div class=\"ft-preview\">\n\n            <div class=\"ft-preview-header\">\n                <strong>Aper\u00e7u<\/strong>\n\n                <span id=\"ftPreviewDimensions\">\n 1 200 \u00d7 1 200\n               <\/span> \n            <\/div>\n\n            <canvas id=\"ftAwardsCanvas\" width=\"1200\" height=\"1200\" aria-label=\"Preview of your personalised French Tech Awards visual\"><\/canvas>\n\n        <\/div>\n\n    <\/div>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Prix \u00ab La French Tech en Allemagne \u00bb 2026 Personnalisez votre visuel de participation Ajoutez votre photo et vos informations, choisissez un format, puis t\u00e9l\u00e9chargez votre visuel personnalis\u00e9 \u00ab \u00bb. 1. Choisissez un format Carr\u00e9 \u2014 1 200 \u00d7 1 200 Horizontal \u2014 2160 \u00d7 1080 Story \/ Reel \u2014 1 080 \u00d7 1 920 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-gradient":""}},"footnotes":""},"categories":[151],"tags":[],"class_list":["post-8246","post","type-post","status-publish","format-standard","hentry","category-recompenses"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"admin","author_link":"https:\/\/www.frenchtechberlin.com\/fr\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Prix \u00ab La French Tech en Allemagne \u00bb 2026 Personnalisez votre visuel de participation Ajoutez votre photo et vos informations, choisissez un format, puis t\u00e9l\u00e9chargez votre visuel personnalis\u00e9 \u00ab \u00bb. 1. Choisissez un format Carr\u00e9 \u2014 1 200 \u00d7 1 200 Horizontal \u2014 2160 \u00d7 1080 Story \/ Reel \u2014 1 080 \u00d7 1 920\u2026","_links":{"self":[{"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/posts\/8246","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/comments?post=8246"}],"version-history":[{"count":21,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/posts\/8246\/revisions"}],"predecessor-version":[{"id":8340,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/posts\/8246\/revisions\/8340"}],"wp:attachment":[{"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/media?parent=8246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/categories?post=8246"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.frenchtechberlin.com\/fr\/wp-json\/wp\/v2\/tags?post=8246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}