{"id":8209,"date":"2026-02-22T18:33:24","date_gmt":"2026-02-22T17:33:24","guid":{"rendered":"https:\/\/bulldogmusik.it\/?page_id=8209"},"modified":"2026-05-31T13:49:06","modified_gmt":"2026-05-31T11:49:06","slug":"bulldogmatch-ita","status":"publish","type":"page","link":"https:\/\/bulldogmusik.it\/en\/bulldogmatch-ita\/","title":{"rendered":"BulldogMatch"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"8209\" class=\"elementor elementor-8209\">\n\t\t\t\t<div class=\"elementor-element elementor-element-61ea7db3 elementor-hidden-desktop elementor-hidden-tablet elementor-hidden-mobile e-flex e-con-boxed wcf-starter-animations-none e-con e-parent\" data-id=\"61ea7db3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-202c3a57 wcf-starter-animations-none elementor-widget elementor-widget-text-editor\" data-id=\"202c3a57\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;wcf_starter_animations&quot;:&quot;none&quot;,&quot;wcf_anim_duration&quot;:1000,&quot;wcf_anim_delay&quot;:0,&quot;wcf_anim_ease&quot;:&quot;ease&quot;}\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>Indovina in rapper in meno di 20 tentativi, <strong>ogni tentativo scopri diverse affinit\u00e0.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-323cb387 elementor-hidden-mobile e-flex e-con-boxed wcf-starter-animations-none e-con e-parent\" data-id=\"323cb387\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5caee3c elementor-widget elementor-widget-wcf--floating-elements\" data-id=\"5caee3c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"wcf--floating-elements.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"wcf--floating-elements\">\r\n\t\t\t                <div class=\"floating-element wcf-live-anim-wiggle elementor-repeater-item-77e55b9   \">\r\n\t\t\t\t\t<img decoding=\"async\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Coppa.png\" alt=\"Coppa\" \/>\t\t\t\t<\/div>\r\n\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-5307d5d1 elementor-hidden-mobile e-flex e-con-boxed wcf-starter-animations-none e-con e-parent\" data-id=\"5307d5d1\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2d47ff14 elementor-widget__width-initial e-n-tabs-mobile elementor-widget elementor-widget-n-tabs\" data-id=\"2d47ff14\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tabs_justify_horizontal&quot;:&quot;center&quot;,&quot;horizontal_scroll&quot;:&quot;disable&quot;}\" data-widget_type=\"nested-tabs.default\">\n\t\t\t\t\t\t\t<div class=\"e-n-tabs\" data-widget-number=\"759693076\" aria-label=\"Tabs. Open items with Enter or Space, close with Escape and navigate using the Arrow keys.\">\n\t\t\t<div class=\"e-n-tabs-heading\" role=\"tablist\">\n\t\t\t\t\t<button id=\"e-n-tab-title-7596930761\" data-tab-title-id=\"e-n-tab-title-7596930761\" class=\"e-n-tab-title\" aria-selected=\"true\" data-tab-index=\"1\" role=\"tab\" tabindex=\"0\" aria-controls=\"e-n-tab-content-7596930761\" style=\"--n-tabs-title-order: 1;\">\n\t\t\t\t\t\t<span class=\"e-n-tab-title-text\">\n\t\t\t\tGiornaliera\t\t\t<\/span>\n\t\t<\/button>\n\t\t\t\t<button id=\"tab-speciale\" data-tab-title-id=\"e-n-tab-title-7596930762\" class=\"e-n-tab-title\" aria-selected=\"false\" data-tab-index=\"2\" role=\"tab\" tabindex=\"-1\" aria-controls=\"e-n-tab-content-7596930762\" style=\"--n-tabs-title-order: 2;\">\n\t\t\t\t\t\t<span class=\"e-n-tab-title-text\">\n\t\t\t\tClassificata\t\t\t<\/span>\n\t\t<\/button>\n\t\t\t\t\t<\/div>\n\t\t\t<div class=\"e-n-tabs-content\">\n\t\t\t\t<div id=\"e-n-tab-content-7596930761\" role=\"tabpanel\" aria-labelledby=\"e-n-tab-title-7596930761\" data-tab-index=\"1\" style=\"--n-tabs-title-order: 1;\" class=\"e-active elementor-element elementor-element-6e384ee7 e-con-full e-flex wcf-starter-animations-none e-con e-child\" data-id=\"6e384ee7\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-2f4558ab e-grid e-con-boxed wcf-starter-animations-none e-con e-child\" data-id=\"2f4558ab\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4abfdee1 elementor-widget__width-initial e-transform elementor-widget elementor-widget-html\" data-id=\"4abfdee1\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_transform_translateX_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:-4,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"it\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>BulldogMatch Demo<\/title>\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Instrument+Sans:wght@400;500;600;700;800&family=Space+Grotesk:wght@500;700&display=swap\" rel=\"stylesheet\">\n  <style>\n    :root {\n      --bg: #000;\n      --panel: #070707;\n      --panel-2: #0b0b0b;\n      --line: rgba(255, 255, 255, 0.08);\n      --line-faint: rgba(255, 255, 255, 0.05);\n      --line-strong: rgba(241, 108, 29, 0.28);\n      --ranked-line-strong: rgba(149, 40, 0, 0.28);\n      --white: #f7f4ef;\n      --copy: rgba(247, 244, 239, 0.76);\n      --copy-soft: rgba(247, 244, 239, 0.56);\n      --copy-faint: rgba(247, 244, 239, 0.34);\n      --accent: #f16c1d;\n      --ranked-accent: #952800;\n      --success: #7dc56a;\n      --warning: #f0b955;\n      --danger: #ff7e59;\n      --ease: 220ms cubic-bezier(.2,.9,.2,1);\n    }\n\n    * {\n      box-sizing: border-box;\n    }\n\n    html,\n    body {\n      margin: 0;\n      min-height: 100%;\n      background: var(--bg);\n      color: var(--white);\n      font-family: \"Instrument Sans\", system-ui, sans-serif;\n      overflow-x: hidden;\n    }\n\n    button,\n    input,\n    iframe {\n      font: inherit;\n    }\n\n    .demo-app {\n      width: min(760px, calc(100vw - 20px));\n      margin: 0 auto;\n      padding: 18px 0 24px;\n    }\n\n    .app-shell {\n      position: relative;\n      isolation: isolate;\n      padding: 14px;\n      border: 1px solid var(--line);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.98), rgba(7, 7, 7, 0.98));\n      overflow: visible;\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n    }\n\n    .app-shell::after {\n      content: \"\";\n      position: absolute;\n      inset: 0;\n      border-radius: inherit;\n      background-image: url(\"data:image\/svg+xml,%3Csvg%20xmlns='http:\/\/www.w3.org\/2000\/svg'%20viewBox='0%200%20180%20180'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='.9'%20numOctaves='3'%20stitchTiles='stitch'\/%3E%3CfeColorMatrix%20type='saturate'%20values='0'\/%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type='table'%20tableValues='0%20.58'\/%3E%3C\/feComponentTransfer%3E%3C\/filter%3E%3Crect%20width='180'%20height='180'%20filter='url(%23n)'%20opacity='1'\/%3E%3C\/svg%3E\");\n      background-size: 180px 180px;\n      mix-blend-mode: overlay;\n      opacity: 0.1;\n      pointer-events: none;\n    }\n\n    .app-shell > * {\n      position: relative;\n      z-index: 1;\n    }\n\n    .topbar {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 0;\n      padding: 10px 8px 14px;\n    }\n\n        .top-logo {\n      display: block;\n      width: min(320px, 58vw);\n      height: auto;\n    }\n    \n    @media (max-width: 768px) {\n  .top-logo {\n    display: none !important;\n  } }\n\n\n    .controls-shell {\n      margin-top: 0;\n      padding: 14px 0 8px;\n      border-top: 1px solid var(--line);\n    }\n\n    .controls-inner {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 8px;\n    }\n\n    .tab-ranked-mobile {\n      display: none !important;\n    }\n\n    .shell {\n      margin-top: 8px;\n      padding-top: 14px;\n      border-top: 1px solid var(--line);\n    }\n\n    .game-shell {\n      position: relative;\n      padding: 2px 0 6px;\n      overflow: visible;\n    }\n\n    .tab,\n    .ghost-button,\n    .button,\n    .secondary-button {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-width: 0;\n      min-height: 36px;\n      padding: 0 10px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--white);\n      font-size: 12px;\n      font-weight: 700;\n      cursor: pointer;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .tab.is-active,\n    .ghost-button.is-active {\n      border-color: var(--line-strong);\n      background: rgba(241, 108, 29, 0.12);\n    }\n\n    .demo-app.is-ranked-mode #rankedModeButton.is-active,\n    .demo-app.is-ranked-mode .ghost-button.is-active {\n      border-color: var(--ranked-line-strong);\n      background: rgba(149, 40, 0, 0.12);\n    }\n\n    .demo-app.is-ranked-mode .hide-when-ranked,\n    body.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    html.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    .demo-app.is-ranked-mode .hide.when.ranked,\n    body.bulldogmatch-ranked-mode-active .hide.when.ranked,\n    html.bulldogmatch-ranked-mode-active .hide.when.ranked {\n      display: none !important;\n    }\n\n    .show-when-ranked,\n    .show.when.ranked,\n    .show-when-ranked-flex,\n    .show.flex.when.ranked,\n    .show-when-ranked-grid,\n    .show.grid.when.ranked,\n    .show-when-ranked-inline {\n      display: none !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked,\n    .demo-app.is-ranked-mode .show.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.when.ranked {\n      display: block !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-flex,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    .demo-app.is-ranked-mode .show.flex.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.flex.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.flex.when.ranked {\n      display: flex !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-grid,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    .demo-app.is-ranked-mode .show.grid.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.grid.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.grid.when.ranked {\n      display: grid !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-inline,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-inline,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-inline {\n      display: inline-flex !important;\n    }\n\n    .button,\n    .secondary-button,\n    .attempts-card {\n      min-width: 152px;\n      min-height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n      font-variation-settings: \"wght\" 700;\n      box-shadow: none;\n    }\n\n    .button,\n    .secondary-button.is-primary {\n      background: var(--accent);\n      border-color: var(--accent);\n      color: #111;\n    }\n\n    .secondary-button.is-muted {\n      background: rgba(255, 255, 255, 0.03);\n      border-color: var(--line);\n      color: var(--white);\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-form .button,\n    .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary {\n      background: var(--ranked-accent);\n      border-color: var(--ranked-accent);\n      color: #111;\n    }\n\n    .button:hover,\n    .secondary-button:hover {\n      background: #fff;\n      border-color: #fff;\n      color: #111;\n    }\n\n    .tab:hover,\n    .ghost-button:hover,\n    .button:hover,\n    .secondary-button:hover,\n    .streak-card.is-guest:hover,\n    .suggestion:hover {\n      transform: translateY(-1px);\n    }\n\n    .button:disabled,\n    .secondary-button:disabled {\n      cursor: not-allowed;\n      opacity: 0.45;\n      transform: none;\n    }\n\n    .help-panel {\n      max-height: 0;\n      overflow: hidden;\n      padding-bottom: 0;\n      border-bottom: 0;\n      transition: max-height 240ms ease, margin 240ms ease, padding-bottom 240ms ease, border-color 240ms ease;\n      margin: 0;\n    }\n\n    .help-panel.is-open {\n      max-height: 1200px;\n      padding-bottom: 12px;\n      border-bottom: 1px solid var(--line);\n      margin: 0 0 12px;\n    }\n\n    .help-panel-mobile-slot {\n      display: none;\n    }\n\n    .help-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n      gap: 10px;\n    }\n\n    .help-card {\n      min-height: 100%;\n      padding: 12px 13px;\n      border: 1px solid var(--line);\n      border-radius: 12px;\n      background: rgba(255, 255, 255, 0.03);\n    }\n\n    .help-card strong {\n      display: block;\n      margin-bottom: 8px;\n      font-size: 13px;\n    }\n\n    .help-card span {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-standard-panel,\n    .match-ranked-panel {\n      display: grid;\n      gap: 12px;\n    }\n\n    .match-ranked-panel.hidden,\n    .match-standard-panel.hidden,\n    .help-panel.hidden,\n    .hidden {\n      display: none !important;\n    }\n\n    .match-meta-grid {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 10px;\n      margin-bottom: 2px;\n    }\n\n    .match-meta-card {\n      min-height: 74px;\n      padding: 12px 14px;\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      background: rgba(255, 255, 255, 0.03);\n      display: flex;\n      flex-direction: column;\n      justify-content: space-between;\n      gap: 8px;\n      text-align: left;\n    }\n\n    .match-meta-grid > .match-meta-card,\n    .match-meta-grid > .ranked-meta-button {\n      align-self: stretch;\n      height: auto;\n    }\n\n    .match-meta-card.is-accent {\n      border-color: rgba(149, 40, 0, 0.24);\n      background: rgba(149, 40, 0, 0.08);\n    }\n\n    .match-meta-label {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.12em;\n      text-transform: uppercase;\n    }\n\n    .match-meta-value {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(24px, 3.2vw, 30px);\n      line-height: 1;\n      letter-spacing: -0.05em;\n    }\n\n    .input-grid {\n      display: grid;\n      grid-template-columns: 152px minmax(0, 1fr) 152px 152px;\n      gap: 10px;\n      align-items: stretch;\n      width: 100%;\n      margin: 0 auto;\n    }\n\n    .attempts-card {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      padding: 0 22px;\n      border: 1px solid #fff;\n      border-radius: 12px;\n      background: #fff;\n      color: #111;\n      text-align: center;\n      white-space: nowrap;\n    }\n\n    .action-slot {\n      width: 152px;\n      min-width: 152px;\n      align-self: stretch;\n    }\n\n    .input-grid > .attempts-card,\n    .input-grid > .button,\n    .action-slot > .secondary-button,\n    .action-slot > .streak-card {\n      width: 100%;\n      min-width: 0;\n      min-height: 56px;\n      height: 56px;\n    }\n\n    .input-grid > .action-slot {\n      align-self: stretch;\n      height: 56px;\n    }\n\n    .input-shell {\n      position: relative;\n      z-index: 5;\n      min-width: 0;\n    }\n\n    .input-label {\n      position: absolute;\n      width: 1px;\n      height: 1px;\n      padding: 0;\n      margin: -1px;\n      overflow: hidden;\n      clip: rect(0, 0, 0, 0);\n      white-space: nowrap;\n      border: 0;\n    }\n\n    .search-field {\n      width: 100%;\n      min-height: 56px;\n      padding: 0 16px;\n      border-radius: 12px;\n      border: 1px solid rgba(255, 255, 255, 0.1);\n      background: #101010;\n      color: var(--white);\n      font-size: 15px;\n      outline: none;\n      transition: border-color var(--ease), background var(--ease), opacity var(--ease);\n    }\n\n    .search-field:focus {\n      border-color: rgba(255, 255, 255, 0.18);\n      background: #111;\n    }\n\n    .search-field::placeholder {\n      color: var(--copy-faint);\n    }\n\n    .search-field:disabled {\n      opacity: 0.5;\n      cursor: not-allowed;\n    }\n\n    .suggestions {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 280px;\n      max-height: min(280px, calc(100dvh - 24px));\n      overflow-y: auto;\n      z-index: 2147483001;\n      display: none;\n      padding: 6px;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: rgba(9, 9, 9, 0.98);\n      box-shadow: 0 22px 50px rgba(0, 0, 0, 0.55);\n      backdrop-filter: blur(12px);\n      overscroll-behavior: contain;\n      -webkit-overflow-scrolling: touch;\n    }\n\n    .suggestion {\n      width: 100%;\n      display: flex;\n      justify-content: space-between;\n      gap: 8px;\n      padding: 11px 12px;\n      border: 0;\n      border-radius: 10px;\n      background: transparent;\n      color: var(--white);\n      text-align: left;\n      cursor: pointer;\n    }\n\n    .suggestion small {\n      color: var(--copy-faint);\n      font-size: 11px;\n    }\n\n    .streak-card {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-height: 56px;\n      height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      border: 1px solid var(--line);\n      background: var(--accent);\n      color: #111;\n      text-decoration: none;\n      text-align: center;\n      cursor: default;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .streak-card.is-guest {\n      cursor: pointer;\n    }\n\n    .streak-copy {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      gap: 3px;\n      min-width: 0;\n    }\n\n    .streak-main {\n      display: block;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n    }\n\n    .streak-sub {\n      display: block;\n      color: rgba(17, 17, 17, 0.74);\n      font-size: 10px;\n      line-height: 1.1;\n      font-weight: 700;\n      letter-spacing: 0;\n      white-space: nowrap;\n    }\n\n    .message {\n      display: none;\n      width: 100%;\n      margin: 10px 0 0;\n      padding: 10px 12px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--copy);\n      font-size: 13px;\n      line-height: 1.45;\n      text-align: center;\n    }\n\n    .message.is-visible {\n      display: block;\n    }\n\n    .message.is-success {\n      border-color: rgba(125, 197, 106, 0.36);\n      background: rgba(125, 197, 106, 0.1);\n      color: #daf2d3;\n    }\n\n    .message.is-error {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .message.is-error-final {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error,\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error-final {\n      border-color: rgba(149, 40, 0, 0.32);\n      background: rgba(149, 40, 0, 0.1);\n      color: #ffd7b5;\n    }\n\n    .results-list {\n      display: grid;\n      gap: 12px;\n      margin-top: 12px;\n      width: 100%;\n    }\n\n    .empty-state {\n      padding: 16px;\n      border: 1px dashed rgba(255, 255, 255, 0.15);\n      border-radius: 12px;\n      color: var(--copy-soft);\n      font-size: 13px;\n      line-height: 1.5;\n      text-align: center;\n    }\n\n    .match-card {\n      padding: 18px;\n      border: 1px solid var(--line-faint);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.96), rgba(10, 10, 10, 0.96));\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n      text-align: left;\n    }\n\n    .match-card.is-solved {\n      border-color: rgba(125, 197, 106, 0.34);\n    }\n\n    .match-card.is-answer {\n      border-color: rgba(241, 108, 29, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-card.is-answer {\n      border-color: rgba(149, 40, 0, 0.34);\n    }\n\n    .match-card.is-auto {\n      border-color: rgba(255, 255, 255, 0.16);\n    }\n\n    .match-top {\n      display: grid;\n      grid-template-columns: 92px minmax(0, 1fr) auto;\n      gap: 16px;\n      align-items: center;\n      margin-bottom: 14px;\n    }\n\n    .match-cover {\n      position: relative;\n      width: 92px;\n      height: 92px;\n      border: 1px solid rgba(255, 255, 255, 0.12);\n      border-radius: 50%;\n      background:\n        radial-gradient(circle at 32% 28%, rgba(241, 108, 29, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(241, 108, 29, 0.16), rgba(255, 255, 255, 0.03));\n      overflow: hidden;\n      box-shadow: 0 18px 34px rgba(0, 0, 0, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-cover {\n      background:\n        radial-gradient(circle at 32% 28%, rgba(149, 40, 0, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(149, 40, 0, 0.16), rgba(255, 255, 255, 0.03));\n    }\n\n    .match-cover img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n\n    .match-cover-fallback {\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: 28px;\n      font-weight: 700;\n      letter-spacing: -0.04em;\n      text-transform: uppercase;\n    }\n\n    .match-copy {\n      min-width: 0;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-start;\n      gap: 10px;\n    }\n\n    .match-badge {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-height: 28px;\n      padding: 0 12px;\n      border: 1px solid var(--line);\n      border-radius: 999px;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 700;\n      letter-spacing: -0.01em;\n    }\n\n    .match-name {\n      margin: 0;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(28px, 4vw, 34px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n    }\n\n    .match-sub {\n      margin: -2px 0 0;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-score {\n      min-width: 112px;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-end;\n      gap: 4px;\n      text-align: right;\n    }\n\n    .match-score-main {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(30px, 4.6vw, 38px);\n      line-height: 1;\n      letter-spacing: -0.06em;\n    }\n\n    .match-score-sub {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.14em;\n      text-transform: uppercase;\n    }\n\n    .match-meter {\n      position: relative;\n      height: 16px;\n      border-radius: 999px;\n      background: rgba(255, 255, 255, 0.06);\n      overflow: hidden;\n    }\n\n    .match-meter-fill {\n      position: absolute;\n      inset: 0 auto 0 0;\n      width: 0;\n      border-radius: inherit;\n      transition: width 320ms ease;\n    }\n\n    .match-spotify {\n      margin-top: 12px;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n    }\n\n    .match-spotify iframe {\n      width: 100%;\n      min-height: 152px;\n      border: 0;\n      border-radius: 14px;\n      background: #0b0b0b;\n      display: block;\n    }\n\n    .match-tip {\n      margin: 12px 0 0;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.55;\n    }\n\n    .ranked-summary-panel {\n      margin-top: 4px;\n      padding: 16px;\n      border: 1px solid rgba(149, 40, 0, 0.28);\n      border-radius: 18px;\n      background: rgba(149, 40, 0, 0.08);\n      width: 100%;\n    }\n\n    .ranked-summary-panel.hidden {\n      display: none !important;\n    }\n\n    .ranked-summary-panel.is-record {\n      border-color: rgba(125, 197, 106, 0.32);\n      background: rgba(125, 197, 106, 0.08);\n    }\n\n    .ranked-summary-title {\n      margin: 0 0 10px;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(22px, 4vw, 28px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n      font-weight: 900;\n    }\n\n    .ranked-summary-lines {\n      display: grid;\n      gap: 5px;\n    }\n\n    .ranked-summary-line {\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.5;\n      font-weight: 600;\n    }\n\n    .ranked-summary-line strong {\n      font-weight: 800;\n    }\n\n    @media (max-width: 720px) {\n      .demo-app {\n        width: 100%;\n        max-width: 100%;\n        padding: max(6px, env(safe-area-inset-top, 0px)) 4px calc(10px + env(safe-area-inset-bottom, 0px));\n      }\n\n      .app-shell {\n        display: flex;\n        flex-direction: column;\n        padding: 8px 6px 10px;\n        border: 0;\n        border-radius: 0;\n        background: transparent;\n        box-shadow: none;\n      }\n\n      .app-shell::after {\n        display: none;\n      }\n\n      .controls-shell {\n        order: 1;\n        position: relative;\n        border-top: 0;\n        border-bottom: 1px solid var(--line);\n        padding-top: 10px;\n        padding-bottom: 20px;\n      }\n\n      .controls-shell::after {\n        content: \"Gioca\";\n        position: absolute;\n        left: 50%;\n        bottom: -11px;\n        transform: translateX(-50%) translateY(-2px);\n        padding: 0 14px;\n        background: var(--bg);\n        color: #9E9E9E;\n        font-size: 15px;\n        line-height: 1;\n      }\n\n      .controls-inner {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .tab-ranked-mobile {\n        display: inline-flex !important;\n      }\n\n      .help-panel-mobile-slot {\n        display: block;\n      }\n\n      .topbar {\n        order: 2;\n        padding: 12px 4px 4px;\n      }\n\n      .top-logo {\n        width: min(260px, 66vw);\n      }\n\n      .shell {\n        order: 3;\n        border-top: 0;\n        padding-top: 14px;\n      }\n\n      .input-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n        width: 100%;\n      }\n\n      .attempts-card {\n        order: 1;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n        padding: 0 10px;\n        font-size: 13px;\n      }\n\n      .action-slot {\n        order: 2;\n        width: auto;\n        min-width: 0;\n      }\n\n      .input-shell {\n        order: 3;\n        grid-column: 1 \/ -1;\n        padding: 0;\n        border: 0;\n        border-radius: 0;\n        background: #0b0b0b;\n      }\n\n      .input-grid > .button {\n        order: 4;\n        grid-column: 1 \/ -1;\n      }\n\n      .input-grid > .attempts-card,\n      .input-grid > .button,\n      .action-slot > .secondary-button,\n      .action-slot > .streak-card,\n      .input-grid > .action-slot {\n        width: 100%;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n      }\n\n      .button,\n      .secondary-button,\n      .streak-card,\n      .attempts-card,\n      .action-slot {\n        min-width: 0;\n      }\n\n      .search-field {\n        min-height: 48px;\n        padding: 0 14px;\n        border: 1px solid rgba(255, 255, 255, 0.14);\n        border-radius: 12px;\n        background: #0b0b0b;\n        font-size: 16px;\n      }\n\n      .suggestion {\n        min-height: 48px;\n        padding: 13px 12px;\n      }\n\n      .help-grid {\n        grid-template-columns: 1fr;\n      }\n\n      .controls-shell .help-panel.is-open {\n        margin: 10px 0 0;\n        padding-bottom: 0;\n        border-bottom-color: transparent;\n      }\n\n      .help-card span {\n        font-size: 13px;\n        line-height: 1.5;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n      }\n\n      .match-meta-grid > .match-meta-card:last-child {\n        grid-column: 1 \/ -1;\n      }\n\n      .demo-app.is-ranked-mode #rankedGamePanel .match-meta-grid {\n        margin-bottom: 0;\n        padding-bottom: 0;\n        border-bottom: 0;\n      }\n\n      .demo-app.is-ranked-mode #rankedGamePanel .match-meta-grid > .match-meta-card:last-child {\n        display: none;\n      }\n\n      .match-meta-card {\n        min-height: 62px;\n        padding: 10px 11px;\n      }\n\n      .match-meta-value {\n        font-size: clamp(18px, 5vw, 24px);\n      }\n\n      .match-top {\n        grid-template-columns: 72px minmax(0, 1fr);\n        gap: 12px;\n      }\n\n      .match-score {\n        grid-column: 1 \/ -1;\n        min-width: 0;\n        align-items: flex-start;\n        text-align: left;\n      }\n\n      .match-copy {\n        gap: 8px;\n      }\n\n      .match-card {\n        padding: 12px;\n        border-radius: 16px;\n      }\n\n      .match-cover {\n        width: 72px;\n        height: 72px;\n      }\n\n      .match-name {\n        font-size: clamp(20px, 7vw, 26px);\n      }\n\n      .match-score-main {\n        font-size: 28px;\n      }\n\n      .message {\n        margin-top: 10px;\n        padding: 9px 11px;\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 520px) {\n      .demo-app {\n        padding-inline: 3px;\n      }\n\n      .controls-shell {\n        padding-top: 8px;\n      }\n\n      .top-logo {\n        width: min(232px, 64vw);\n      }\n\n      .shell {\n        padding-top: 8px;\n      }\n\n      .controls-inner {\n        grid-template-columns: 1fr;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .match-sub {\n        font-size: 11px;\n      }\n\n      .match-spotify iframe {\n        min-height: 152px;\n      }\n\n      .match-tip {\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 400px) {\n      .demo-app {\n        padding-inline: 2px;\n      }\n\n      .top-logo {\n        width: min(214px, 62vw);\n      }\n\n      .attempts-card,\n      .button,\n      .secondary-button,\n      .streak-main {\n        font-size: 12px;\n      }\n\n      .streak-sub {\n        font-size: 8px;\n      }\n    }\n\n    @media (hover: none), (pointer: coarse) {\n      .tab:hover,\n      .ghost-button:hover,\n      .button:hover,\n      .secondary-button:hover,\n      .streak-card.is-guest:hover,\n      .suggestion:hover {\n        transform: none;\n      }\n\n      .button:hover,\n      .secondary-button:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .demo-app.is-ranked-mode .match-ranked-form .button:hover,\n      .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary:hover {\n        background: var(--ranked-accent);\n        border-color: var(--ranked-accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover .streak-sub {\n        color: rgba(17, 17, 17, 0.74);\n      }\n\n      .suggestion:hover {\n        background: transparent;\n      }\n\n      .button:active,\n      .secondary-button:active,\n      .streak-card.is-guest:active,\n      .tab:active,\n      .ghost-button:active,\n      .suggestion:active {\n        transform: scale(0.99);\n      }\n    }\n\n    @media (max-width: 720px) {\n      .demo-app .top-logo {\n        margin-top: 14px !important;\n      }\n\n      .demo-app .controls-inner {\n        margin-bottom: 18px !important;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"demo-app\">\n    <div class=\"app-shell\">\n      <header class=\"topbar\">\n        <img decoding=\"async\" class=\"top-logo\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png\" alt=\"BulldogMatch\">\n      <\/header>\n\n      <section class=\"controls-shell\" aria-label=\"Controlli BulldogMatch\">\n        <div class=\"controls-inner\">\n          <button class=\"tab is-active\" data-mode=\"classic\" type=\"button\">Daily<\/button>\n          <button class=\"tab\" data-mode=\"practice\" type=\"button\">Allenamento<\/button>\n          <button id=\"rankedModeButton\" class=\"tab tab-ranked-mobile\" type=\"button\">Classificata<\/button>\n          <button id=\"helpToggle\" class=\"ghost-button\" type=\"button\">Come si gioca<\/button>\n        <\/div>\n        <div id=\"helpPanelMobileSlot\" class=\"help-panel-mobile-slot\"><\/div>\n      <\/section>\n\n      <main class=\"shell\">\n        <section class=\"game-shell\">\n          <section id=\"helpPanel\" class=\"help-panel\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Obiettivo<\/strong>\n                <span>Trova l'artista segreto leggendo il punteggio di affinit\u00e0 di ogni tentativo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Affinit\u00e0<\/strong>\n                <span>0-100%. Pi\u00f9 il numero sale, pi\u00f9 il guess condivide tratti con il target.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Cosa Conta davvero<\/strong>\n                <span>Crew, collab, stile, sottogenere, regione di nascita, artisti simili, debutto e tipologia.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Daily<\/strong>\n                <span>Hai 20 tentativi. La partita del giorno si salva in locale.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Allenamento<\/strong>\n                <span>Fai pratica, puoi cambiare artista quando vuoi con il pulsante NUOVO.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Lettura veloce<\/strong>\n                <span>\ud83d\udfe2 = molto vicino. <br>\ud83d\udfe1 = buona base. <br>\ud83d\udfe0 = tracce utili. <\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedHelpPanel\" class=\"help-panel hidden\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Timer<\/strong>\n                <span>Hai 5 minuti per fare il punteggio pi\u00f9 alto possibile.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Punti<\/strong>\n                <span>Ogni artista corretto vale 1 punto e aggiunge 30 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Skip<\/strong>\n                <span>Puoi saltare il round corrente, ma perdi 10 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Tentativi<\/strong>\n                <span>Hai 20 tentativi per artista. I guess restano ordinati per affinit\u00e0.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Reveal gratis<\/strong>\n                <span>L'artista corretto del round appena chiuso diventa l'indizio iniziale del round dopo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Record<\/strong>\n                <span>Quando il backend ranked \u00e8 presente, il miglior punteggio viene salvato come nell'originale.<\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"standardGamePanel\" class=\"match-standard-panel\">\n            <form id=\"guessForm\" class=\"input-grid\" autocomplete=\"off\">\n              <div id=\"attemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label id=\"inputLabel\" class=\"input-label\" for=\"artistInput\">inserisci un artista<\/label>\n                <input id=\"artistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"gameMsg\">\n                <div id=\"suggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti\"><\/div>\n              <\/div>\n\n              <button id=\"submitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"modeActionButton\" class=\"secondary-button is-primary hidden\" type=\"button\">NUOVO<\/button>\n                <a id=\"dailyStreakCard\" class=\"streak-card hidden is-guest\" href=\"https:\/\/bulldogmusik.it\/account-login\/\" aria-label=\"Accedi per salvare la streak\">\n                  <span class=\"streak-copy\">\n                    <span id=\"dailyStreakMain\" class=\"streak-main\">STREAK<\/span>\n                    <span id=\"dailyStreakSub\" class=\"streak-sub\">Accedi per salvarla<\/span>\n                  <\/span>\n                <\/a>\n              <\/div>\n            <\/form>\n\n            <div id=\"gameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"resultsList\" class=\"results-list\">\n              <div class=\"empty-state\">Nessun tentativo ancora.<\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedGamePanel\" class=\"match-ranked-panel hidden\">\n            <section class=\"match-meta-grid\" aria-label=\"Statistiche Classificata\">\n              <article class=\"match-meta-card is-accent\">\n                <span class=\"match-meta-label\">Tempo<\/span>\n                <strong id=\"rankedTimerValue\" class=\"match-meta-value\">5:00<\/strong>\n              <\/article>\n              <article class=\"match-meta-card\">\n                <span class=\"match-meta-label\">Punti<\/span>\n                <strong id=\"rankedScoreValue\" class=\"match-meta-value\">0<\/strong>\n              <\/article>\n              <article class=\"match-meta-card\">\n                <span class=\"match-meta-label\">Round<\/span>\n                <strong id=\"rankedRoundValue\" class=\"match-meta-value\">Pronto<\/strong>\n              <\/article>\n            <\/section>\n\n            <form id=\"rankedGuessForm\" class=\"input-grid match-ranked-form\" autocomplete=\"off\">\n              <div id=\"rankedAttemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label class=\"input-label\" for=\"rankedArtistInput\">inserisci un artista<\/label>\n                <input id=\"rankedArtistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"rankedGameMsg\">\n                <div id=\"rankedSuggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti classificata\"><\/div>\n              <\/div>\n\n              <button id=\"rankedSubmitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"rankedSideActionButton\" class=\"secondary-button is-primary\" type=\"button\">INIZIA<\/button>\n              <\/div>\n            <\/form>\n\n            <section id=\"rankedSummaryPanel\" class=\"ranked-summary-panel hidden\" aria-live=\"polite\">\n              <h3 id=\"rankedSummaryTitle\" class=\"ranked-summary-title\">Partita conclusa<\/h3>\n              <div id=\"rankedSummaryLines\" class=\"ranked-summary-lines\"><\/div>\n            <\/section>\n\n            <div id=\"rankedGameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"rankedResultsList\" class=\"results-list\">\n              <div class=\"empty-state\">ACCEDI per giocare.<\/div>\n            <\/div>\n          <\/section>\n        <\/section>\n      <\/main>\n    <\/div>\n  <\/div>\n\n  <script>\n    (async function () {\n      const bootstrapScript = document.currentScript;\n\n      async function loadScriptSource(src) {\n        return new Promise((resolve) => {\n          const existing = Array.from(document.querySelectorAll(\"script[src]\")).find((node) => node.src === src);\n          if (existing && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            resolve(true);\n            return;\n          }\n\n          const script = document.createElement(\"script\");\n          script.src = src;\n          script.async = false;\n          script.onload = () => resolve(true);\n          script.onerror = () => resolve(false);\n          document.head.appendChild(script);\n        });\n      }\n\n      async function ensureArtistsLoaded() {\n        if (Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n          return window.bulldogleArtists;\n        }\n\n        const sources = [\n          \".\/artists.js\",\n          \"artists.js\",\n          \"\/bulldogle\/artists.js?v=31\"\n        ];\n\n        if (window.location && \/^https?:\/i.test(window.location.protocol) && window.location.origin) {\n          sources.push(window.location.origin + \"\/bulldogle\/artists.js?v=31\");\n        }\n\n        sources.push(\"https:\/\/bulldogmusik.it\/bulldogle\/artists.js?v=31\");\n\n        for (const source of sources) {\n          const loaded = await loadScriptSource(source);\n          if (loaded && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            return window.bulldogleArtists;\n          }\n        }\n\n        return [];\n      }\n\n      const artistsSource = await ensureArtistsLoaded();\n      const matchConfig = window.bulldogleMatchConfig || {};\n      const rankedConfig = window.bulldogleRankedConfig || {};\n      const guestLoginUrl = \"https:\/\/bulldogmusik.it\/account-login\/\";\n      const CLASSIC_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png\";\n      const RANKED_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1.png\";\n      const STANDARD_ATTEMPTS = 20;\n      const RANKED_ATTEMPTS = 20;\n      const RANKED_INITIAL_TIME = 300;\n      const RANKED_CORRECT_BONUS = 30;\n      const RANKED_SKIP_PENALTY = 10;\n      const RANKED_ROUND_TRANSITION_MS = 1000;\n      const RANKED_GAME_KEY = \"match\";\n      const hasRankedBackend = Boolean(rankedConfig && rankedConfig.ajaxUrl && rankedConfig.nonce);\n      const spotifyThumbStorageKey = \"bulldogmatch-spotify-thumbs-v1\";\n\n      const regionCoords = {\n        \"Abruzzo\": { lat: 42.3, lon: 13.9 },\n        \"Basilicata\": { lat: 40.6, lon: 15.8 },\n        \"Calabria\": { lat: 38.9, lon: 16.6 },\n        \"Campania\": { lat: 40.8, lon: 14.3 },\n        \"Emilia-Romagna\": { lat: 44.5, lon: 11.3 },\n        \"Friuli-Venezia Giulia\": { lat: 46.1, lon: 13.1 },\n        \"Lazio\": { lat: 41.9, lon: 12.5 },\n        \"Liguria\": { lat: 44.4, lon: 8.9 },\n        \"Lombardia\": { lat: 45.5, lon: 9.2 },\n        \"Marche\": { lat: 43.6, lon: 13.5 },\n        \"Molise\": { lat: 41.6, lon: 14.6 },\n        \"Piemonte\": { lat: 45.1, lon: 7.7 },\n        \"Puglia\": { lat: 41.1, lon: 16.9 },\n        \"Sardegna\": { lat: 40.1, lon: 9.0 },\n        \"Sicilia\": { lat: 37.6, lon: 14.0 },\n        \"Toscana\": { lat: 43.8, lon: 11.2 },\n        \"Trentino-Alto Adige\": { lat: 46.5, lon: 11.3 },\n        \"Umbria\": { lat: 43.1, lon: 12.4 },\n        \"Valle d'Aosta\": { lat: 45.7, lon: 7.3 },\n        \"Veneto\": { lat: 45.4, lon: 12.3 },\n        \"Estero\": null\n      };\n\n      const spotifyThumbCache = new Map();\n      const spotifyThumbRequests = new Map();\n      let standardMessageTimeoutId = 0;\n      let rankedMessageTimeoutId = 0;\n      let dailyStreakLoading = false;\n      let dailyStreakLoaded = false;\n\n      try {\n        const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n        Object.keys(storedThumbs).forEach((spotifyId) => {\n          if (storedThumbs[spotifyId]) {\n            spotifyThumbCache.set(spotifyId, String(storedThumbs[spotifyId]));\n          }\n        });\n      } catch (_) {}\n\n      const artists = artistsSource.map((artist) => ({\n        id: Number(artist.id),\n        name: String(artist.name || \"\").trim(),\n        city: String(artist.city || \"\").trim(),\n        region: String(artist.region || \"\").trim(),\n        debutYear: Number(artist.debutYear || 0),\n        members: String(artist.members || \"\").trim(),\n        gender: String(artist.gender || \"\").trim(),\n        popularity: String(artist.popularity || \"\").trim().toLowerCase(),\n        spotifyId: String(artist.spotifyId || \"\").trim(),\n        crew: String(artist.crew || \"\").trim(),\n        style: String(artist.style || \"\").trim(),\n        collab: String(artist.collab || \"\").trim(),\n        subgenre: String(artist.subgenre || \"\").trim(),\n        collabmid: String(artist.collabmid || \"\").trim(),\n        similar: String(artist.similar || \"\").trim(),\n        birthYear: Number(artist.birthYear || 0),\n        tip: String(artist.tip || \"\").trim()\n      })).filter((artist) => artist.id && artist.name);\n\n      const demoRoots = Array.from(document.querySelectorAll(\".demo-app\"));\n      const appRoot = bootstrapScript &&\n        bootstrapScript.previousElementSibling &&\n        bootstrapScript.previousElementSibling.classList &&\n        bootstrapScript.previousElementSibling.classList.contains(\"demo-app\")\n          ? bootstrapScript.previousElementSibling\n          : demoRoots[demoRoots.length - 1];\n\n      if (!appRoot) return;\n\n      const elements = {\n        topLogo: appRoot.querySelector(\".top-logo\"),\n        tabs: Array.from(appRoot.querySelectorAll(\".tab[data-mode]\")),\n        rankedModeButton: appRoot.querySelector(\"#rankedModeButton\"),\n        helpToggle: appRoot.querySelector(\"#helpToggle\"),\n        helpPanelMobileSlot: appRoot.querySelector(\"#helpPanelMobileSlot\"),\n        helpPanel: appRoot.querySelector(\"#helpPanel\"),\n        rankedHelpPanel: appRoot.querySelector(\"#rankedHelpPanel\"),\n        standardGamePanel: appRoot.querySelector(\"#standardGamePanel\"),\n        rankedGamePanel: appRoot.querySelector(\"#rankedGamePanel\"),\n        attemptsCard: appRoot.querySelector(\"#attemptsCard\"),\n        guessForm: appRoot.querySelector(\"#guessForm\"),\n        inputLabel: appRoot.querySelector(\"#inputLabel\"),\n        input: appRoot.querySelector(\"#artistInput\"),\n        suggestions: appRoot.querySelector(\"#suggestions\"),\n        submitButton: appRoot.querySelector(\"#submitButton\"),\n        modeActionButton: appRoot.querySelector(\"#modeActionButton\"),\n        dailyStreakCard: appRoot.querySelector(\"#dailyStreakCard\"),\n        dailyStreakMain: appRoot.querySelector(\"#dailyStreakMain\"),\n        dailyStreakSub: appRoot.querySelector(\"#dailyStreakSub\"),\n        message: appRoot.querySelector(\"#gameMsg\"),\n        resultsList: appRoot.querySelector(\"#resultsList\"),\n        rankedTimerValue: appRoot.querySelector(\"#rankedTimerValue\"),\n        rankedScoreValue: appRoot.querySelector(\"#rankedScoreValue\"),\n        rankedRoundValue: appRoot.querySelector(\"#rankedRoundValue\"),\n        rankedAttemptsCard: appRoot.querySelector(\"#rankedAttemptsCard\"),\n        rankedGuessForm: appRoot.querySelector(\"#rankedGuessForm\"),\n        rankedInput: appRoot.querySelector(\"#rankedArtistInput\"),\n        rankedSuggestions: appRoot.querySelector(\"#rankedSuggestions\"),\n        rankedSubmitButton: appRoot.querySelector(\"#rankedSubmitButton\"),\n        rankedSideActionButton: appRoot.querySelector(\"#rankedSideActionButton\"),\n        rankedSummaryPanel: appRoot.querySelector(\"#rankedSummaryPanel\"),\n        rankedSummaryTitle: appRoot.querySelector(\"#rankedSummaryTitle\"),\n        rankedSummaryLines: appRoot.querySelector(\"#rankedSummaryLines\"),\n        rankedMessage: appRoot.querySelector(\"#rankedGameMsg\"),\n        rankedResultsList: appRoot.querySelector(\"#rankedResultsList\")\n      };\n\n      if (!elements.guessForm || !elements.input || !elements.submitButton || !elements.suggestions) {\n        return;\n      }\n\n      const desktopHelpPanelParent = elements.helpPanel ? elements.helpPanel.parentElement : null;\n      const desktopHelpPanelNextSibling = elements.helpPanel ? elements.helpPanel.nextElementSibling : null;\n      const desktopRankedHelpPanelParent = elements.rankedHelpPanel ? elements.rankedHelpPanel.parentElement : null;\n      const desktopRankedHelpPanelNextSibling = elements.rankedHelpPanel ? elements.rankedHelpPanel.nextElementSibling : null;\n\n      if (elements.suggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.suggestions);\n      }\n\n      if (elements.rankedSuggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.rankedSuggestions);\n      }\n\n      const italyDateInfo = getItalyDateInfo();\n      const dailyStorageKey = \"bulldogle_affinity_\" + italyDateInfo.dayKey;\n      const rankedLocalBestKey = \"bulldogmatch_ranked_demo_best\";\n\n      const app = {\n        mode: \"classic\",\n        modes: {\n          classic: createStandardState(getDailyTarget()),\n          practice: createPracticeState()\n        },\n        ranked: createRankedState()\n      };\n\n      if (!artists.length) {\n        elements.message.textContent = \"artists.js non e stato caricato correttamente.\";\n        elements.message.classList.add(\"is-visible\", \"is-error\");\n        disableStandardInput(true);\n        return;\n      }\n\n      function createStandardState(targetArtist) {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: targetArtist\n        };\n      }\n\n      function createPracticeState() {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: 1,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n      }\n\n      function createRankedState() {\n        return {\n          active: false,\n          preparing: false,\n          submitting: false,\n          complete: false,\n          betweenRounds: false,\n          sessionToken: \"\",\n          currentArtist: null,\n          score: 0,\n          timeLeft: RANKED_INITIAL_TIME,\n          roundAttempts: 0,\n          roundGuessedIds: new Set(),\n          usedTargetIds: new Set(),\n          freeHintArtist: null,\n          results: [],\n          timerId: 0,\n          roundTransitionId: 0,\n          summaryTitle: \"Partita conclusa\",\n          summaryLines: [],\n          isRecord: false,\n          localBest: readLocalRankedBest()\n        };\n      }\n\n      function readLocalRankedBest() {\n        try {\n          return Number(window.localStorage.getItem(rankedLocalBestKey) || 0);\n        } catch (_) {\n          return 0;\n        }\n      }\n\n      function writeLocalRankedBest(score) {\n        const best = Math.max(readLocalRankedBest(), Number(score || 0));\n        try {\n          window.localStorage.setItem(rankedLocalBestKey, String(best));\n        } catch (_) {}\n        return best;\n      }\n\n      function normalize(value) {\n        return String(value || \"\").trim().toLowerCase();\n      }\n\n      function getItalyDateInfo() {\n        const formatter = new Intl.DateTimeFormat(\"en-CA\", {\n          timeZone: \"Europe\/Rome\",\n          year: \"numeric\",\n          month: \"2-digit\",\n          day: \"2-digit\"\n        });\n\n        const parts = formatter.formatToParts(new Date());\n        const year = Number(parts.find((part) => part.type === \"year\").value);\n        const month = Number(parts.find((part) => part.type === \"month\").value);\n        const day = Number(parts.find((part) => part.type === \"day\").value);\n\n        return {\n          dayKey: year + \"-\" + String(month).padStart(2, \"0\") + \"-\" + String(day).padStart(2, \"0\"),\n          dayIndex: Math.floor(Date.UTC(year, month - 1, day) \/ 86400000)\n        };\n      }\n\n      function getDailyTarget() {\n        return artists[(italyDateInfo.dayIndex + 137) % artists.length] || artists[0];\n      }\n\n      function pickRandomArtist(excludedIds) {\n        const blocked = excludedIds || new Set();\n        let pool = artists.filter((artist) => !blocked.has(artist.id));\n        if (!pool.length) pool = artists.slice();\n        return pool[Math.floor(Math.random() * pool.length)];\n      }\n\n      function getCurrentStandardState() {\n        return app.modes[app.mode];\n      }\n\n      function findArtistByName(name) {\n        const normalized = normalize(name);\n        return artists.find((artist) => normalize(artist.name) === normalized);\n      }\n\n      function normalizeCommaSeparatedList(listString) {\n        if (!listString) return [];\n        return listString\n          .split(\",\")\n          .map((item) => item.trim().toLowerCase())\n          .filter(Boolean);\n      }\n\n      function isInList(artistName, listString) {\n        if (!listString || !artistName) return false;\n        return normalizeCommaSeparatedList(listString).includes(artistName.toLowerCase());\n      }\n\n      function sharesListValue(firstListString, secondListString) {\n        const firstList = normalizeCommaSeparatedList(firstListString);\n        const secondList = normalizeCommaSeparatedList(secondListString);\n        if (!firstList.length || !secondList.length) return false;\n\n        const secondSet = new Set(secondList);\n        return firstList.some((item) => secondSet.has(item));\n      }\n\n      function calculateAffinity(guess, target) {\n        if (guess.id === target.id) return 100;\n\n        let score = 0;\n        const W_CREW = 24;\n        const W_COLLAB = 21;\n        const W_STYLE = 16;\n        const W_SUBGENRE = 10;\n        const W_REGION = 3;\n        const W_COLLABMID = 15;\n        const W_SIMILAR = 19;\n        const W_DEBUT_YEAR = 3;\n        const W_MEMBERS = 3;\n        const W_BIRTH_YEAR = 0;\n\n        if (sharesListValue(guess.crew, target.crew)) score += W_CREW;\n        if (isInList(target.name, guess.collab) || isInList(guess.name, target.collab)) score += W_COLLAB;\n        if (guess.style && target.style && guess.style === target.style) score += W_STYLE;\n        if (guess.subgenre && target.subgenre && guess.subgenre === target.subgenre) score += W_SUBGENRE;\n\n        if (guess.city === target.city) {\n          score += W_REGION;\n        } else if (guess.region === target.region) {\n          score += W_REGION * 0.6;\n        } else {\n          const g = regionCoords[guess.region];\n          const t = regionCoords[target.region];\n          if (g && t) {\n            const latDiff = g.lat - t.lat;\n            const lonDiff = g.lon - t.lon;\n            const dist = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);\n            const maxDist = 12;\n            if (dist < maxDist) {\n              score += (W_REGION * 0.3) * (1 - dist \/ maxDist);\n            }\n          }\n        }\n\n        if (isInList(target.name, guess.collabmid) || isInList(guess.name, target.collabmid)) score += W_COLLABMID;\n\n        const yearDiff = Math.abs(Number(guess.debutYear || 0) - Number(target.debutYear || 0));\n        if (yearDiff === 0) score += W_DEBUT_YEAR;\n        else if (yearDiff <= 2) score += W_DEBUT_YEAR * 0.8;\n        else if (yearDiff <= 5) score += W_DEBUT_YEAR * 0.5;\n        else if (yearDiff <= 10) score += W_DEBUT_YEAR * 0.2;\n\n        if (guess.members === target.members) score += W_MEMBERS;\n        if (isInList(target.name, guess.similar) || isInList(guess.name, target.similar)) score += W_SIMILAR;\n\n        if (guess.birthYear && target.birthYear) {\n          const birthDiff = Math.abs(Number(guess.birthYear || 0) - Number(target.birthYear || 0));\n          if (birthDiff === 0) score += W_BIRTH_YEAR;\n          else if (birthDiff <= 2) score += W_BIRTH_YEAR * 0.8;\n          else if (birthDiff <= 5) score += W_BIRTH_YEAR * 0.5;\n          else if (birthDiff <= 10) score += W_BIRTH_YEAR * 0.2;\n        }\n\n        return Math.min(99, Math.floor(score));\n      }\n\n      function getAffinityTone(affinity) {\n        if (affinity >= 100) return \"perfect\";\n        if (affinity >= 80) return \"high\";\n        if (affinity >= 50) return \"medium\";\n        if (affinity >= 30) return \"warm\";\n        return \"low\";\n      }\n\n      function getAffinityColor(affinity, ranked) {\n        if (affinity >= 100) return \"#7dc56a\";\n        if (affinity >= 80) return \"#7dc56a\";\n        if (affinity >= 50) return \"#f0b955\";\n        if (affinity >= 30) return ranked ? \"#952800\" : \"#f16c1d\";\n        return \"#ff7e59\";\n      }\n\n      function getAffinityDescriptor(affinity) {\n        if (affinity >= 100) return \"Hai indovinato\";\n        if (affinity >= 80) return \"Molto vicino\";\n        if (affinity >= 50) return \"Buona affinit\u00e0\";\n        if (affinity >= 30) return \"Tracce interessanti\";\n        return \"Molto lontano\";\n      }\n\n      function getArtistInitials(name) {\n        const parts = String(name || \"\").trim().split(\/\\s+\/).filter(Boolean);\n        if (!parts.length) return \"BM\";\n        return parts.slice(0, 2).map((part) => part.charAt(0)).join(\"\");\n      }\n\n      function persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl) {\n        if (!spotifyId || !thumbnailUrl) return;\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          storedThumbs[spotifyId] = thumbnailUrl;\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      function clearSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return;\n        spotifyThumbCache.delete(spotifyId);\n        spotifyThumbRequests.delete(spotifyId);\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          delete storedThumbs[spotifyId];\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      async function getSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return \"\";\n        if (spotifyThumbCache.has(spotifyId)) return spotifyThumbCache.get(spotifyId);\n        if (spotifyThumbRequests.has(spotifyId)) return spotifyThumbRequests.get(spotifyId);\n\n        const request = (async function () {\n          const sources = [\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"https:\/\/open.spotify.com\/artist\/\" + spotifyId),\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"spotify:artist:\" + spotifyId)\n          ];\n\n          for (const source of sources) {\n            try {\n              const response = await fetch(source, { credentials: \"omit\" });\n              if (!response.ok) continue;\n              const payload = await response.json();\n              const thumbnailUrl = payload && payload.thumbnail_url ? String(payload.thumbnail_url) : \"\";\n              if (thumbnailUrl) {\n                spotifyThumbCache.set(spotifyId, thumbnailUrl);\n                persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl);\n                return thumbnailUrl;\n              }\n            } catch (_) {}\n          }\n\n          return \"\";\n        })().finally(function () {\n          spotifyThumbRequests.delete(spotifyId);\n        });\n\n        spotifyThumbRequests.set(spotifyId, request);\n        return request;\n      }\n\n      function buildArtistCover(artist) {\n        const cover = document.createElement(\"div\");\n        cover.className = \"match-cover\";\n\n        const fallback = document.createElement(\"span\");\n        fallback.className = \"match-cover-fallback\";\n        fallback.textContent = getArtistInitials(artist.name);\n        cover.appendChild(fallback);\n\n        if (artist.spotifyId) {\n          getSpotifyThumbnailUrl(artist.spotifyId).then((thumbnailUrl) => {\n            if (!thumbnailUrl) return;\n            const image = document.createElement(\"img\");\n            image.alt = artist.name;\n            image.loading = \"eager\";\n            image.decoding = \"async\";\n            image.referrerPolicy = \"no-referrer\";\n\n            function applyThumbnail() {\n              cover.replaceChildren(image);\n            }\n\n            function handleThumbnailError() {\n              clearSpotifyThumbnailUrl(artist.spotifyId);\n            }\n\n            image.addEventListener(\"error\", handleThumbnailError, { once: true });\n            image.addEventListener(\"load\", applyThumbnail, { once: true });\n            image.src = thumbnailUrl;\n\n            if (image.complete) {\n              if (image.naturalWidth > 0) {\n                applyThumbnail();\n              } else {\n                handleThumbnailError();\n              }\n            }\n          });\n        }\n\n        return cover;\n      }\n\n      function buildSpotifyEmbed(spotifyId) {\n        if (!spotifyId) return null;\n        const wrap = document.createElement(\"div\");\n        wrap.className = \"match-spotify\";\n        const iframe = document.createElement(\"iframe\");\n        iframe.src = \"https:\/\/open.spotify.com\/embed\/artist\/\" + spotifyId + \"?utm_source=generator&theme=0\";\n        iframe.allow = \"autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture\";\n        iframe.loading = \"lazy\";\n        wrap.appendChild(iframe);\n        return wrap;\n      }\n\n      function buildArtistTip(tip) {\n        const cleanTip = String(tip || \"\").trim();\n        if (!cleanTip) return null;\n        const copy = document.createElement(\"p\");\n        copy.className = \"match-tip\";\n        copy.textContent = cleanTip;\n        return copy;\n      }\n\n      function buildResultEntry(artist, targetArtist, meta) {\n        const affinity = calculateAffinity(artist, targetArtist);\n        return {\n          artist: artist,\n          affinity: affinity,\n          kind: meta.kind || \"guess\",\n          label: meta.label || \"Tentativo\",\n          subcopy: meta.subcopy || getAffinityDescriptor(affinity),\n          solved: artist.id === targetArtist.id,\n          spotifyId: meta.spotifyId || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.spotifyId : \"\"),\n          tip: meta.tip || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.tip : \"\"),\n          ranked: Boolean(meta.ranked)\n        };\n      }\n\n      function buildMatchResultCard(result) {\n        const article = document.createElement(\"article\");\n        article.className = \"match-card\";\n        if (result.solved) article.classList.add(\"is-solved\");\n        if (result.kind === \"answer\") article.classList.add(\"is-answer\");\n        if (result.kind === \"auto\") article.classList.add(\"is-auto\");\n\n        const top = document.createElement(\"div\");\n        top.className = \"match-top\";\n\n        const cover = buildArtistCover(result.artist);\n        const copy = document.createElement(\"div\");\n        copy.className = \"match-copy\";\n\n        const badge = document.createElement(\"span\");\n        badge.className = \"match-badge\";\n        badge.textContent = result.label;\n\n        const name = document.createElement(\"h4\");\n        name.className = \"match-name\";\n        name.textContent = result.artist.name;\n\n        const sub = document.createElement(\"p\");\n        sub.className = \"match-sub\";\n        sub.textContent = result.subcopy || getAffinityDescriptor(result.affinity);\n\n        copy.appendChild(badge);\n        copy.appendChild(name);\n        copy.appendChild(sub);\n\n        const score = document.createElement(\"div\");\n        score.className = \"match-score\";\n\n        const scoreMain = document.createElement(\"span\");\n        scoreMain.className = \"match-score-main\";\n        scoreMain.textContent = result.affinity + \"%\";\n\n        const scoreSub = document.createElement(\"span\");\n        scoreSub.className = \"match-score-sub\";\n        scoreSub.textContent = \"Affinit\u00e0\";\n\n        score.appendChild(scoreMain);\n        score.appendChild(scoreSub);\n\n        top.appendChild(cover);\n        top.appendChild(copy);\n        top.appendChild(score);\n\n        const meter = document.createElement(\"div\");\n        meter.className = \"match-meter\";\n        const fill = document.createElement(\"div\");\n        fill.className = \"match-meter-fill\";\n        fill.style.width = result.affinity + \"%\";\n        fill.style.background = getAffinityColor(result.affinity, result.ranked);\n        meter.appendChild(fill);\n\n        article.appendChild(top);\n        article.appendChild(meter);\n\n        const spotifyEmbed = buildSpotifyEmbed(result.spotifyId);\n        if (spotifyEmbed) article.appendChild(spotifyEmbed);\n        if (result.tip) {\n          const tipNode = buildArtistTip(result.tip);\n          if (tipNode) article.appendChild(tipNode);\n        }\n\n        return article;\n      }\n\n      function disableStandardInput(disabled) {\n        elements.input.disabled = disabled;\n        elements.submitButton.disabled = disabled;\n      }\n\n      function disableRankedInput(disabled) {\n        elements.rankedInput.disabled = disabled;\n        elements.rankedSubmitButton.disabled =\n          disabled ||\n          !app.ranked.active ||\n          app.ranked.betweenRounds ||\n          app.ranked.preparing ||\n          app.ranked.submitting;\n      }\n\n      function getViewportMetrics() {\n        if (window.visualViewport) {\n          return {\n            width: window.visualViewport.width,\n            height: window.visualViewport.height,\n            offsetLeft: window.visualViewport.offsetLeft,\n            offsetTop: window.visualViewport.offsetTop\n          };\n        }\n\n        return {\n          width: window.innerWidth,\n          height: window.innerHeight,\n          offsetLeft: 0,\n          offsetTop: 0\n        };\n      }\n\n      function positionSuggestionsFor(inputElement, suggestionElement) {\n        const rect = inputElement.getBoundingClientRect();\n        const viewport = getViewportMetrics();\n        const viewportPadding = 10;\n        const width = Math.min(rect.width, viewport.width - viewportPadding * 2);\n        const left = Math.max(\n          viewport.offsetLeft + viewportPadding,\n          Math.min(rect.left, viewport.offsetLeft + viewport.width - width - viewportPadding)\n        );\n        const spaceBelow = viewport.offsetTop + viewport.height - rect.bottom - viewportPadding;\n        const maxHeight = Math.max(140, Math.min(280, spaceBelow - 8));\n\n        suggestionElement.style.width = width + \"px\";\n        suggestionElement.style.left = left + \"px\";\n        suggestionElement.style.top = Math.max(viewportPadding, rect.bottom + 8) + \"px\";\n        suggestionElement.style.maxHeight = maxHeight + \"px\";\n      }\n\n      function hideSuggestions() {\n        elements.suggestions.style.display = \"none\";\n      }\n\n      function showSuggestions() {\n        positionSuggestionsFor(elements.input, elements.suggestions);\n        elements.suggestions.style.display = \"block\";\n      }\n\n      function hideRankedSuggestions() {\n        elements.rankedSuggestions.style.display = \"none\";\n      }\n\n      function showRankedSuggestions() {\n        positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        elements.rankedSuggestions.style.display = \"block\";\n      }\n\n      function getCurrentHelpPanel() {\n        return app.mode === \"ranked\" ? elements.rankedHelpPanel : elements.helpPanel;\n      }\n\n      function closeAllHelpPanels() {\n        [elements.helpPanel, elements.rankedHelpPanel].forEach((panel) => {\n          if (!panel) return;\n          panel.classList.remove(\"is-open\");\n          panel.setAttribute(\"aria-hidden\", \"true\");\n        });\n        elements.helpToggle.classList.remove(\"is-active\");\n      }\n\n      function syncHelpToggleState() {\n        const panel = getCurrentHelpPanel();\n        elements.helpToggle.classList.toggle(\"is-active\", !!panel && panel.classList.contains(\"is-open\"));\n      }\n\n      function restorePanelPlacement(panel, parent, nextSibling) {\n        if (!panel || !parent) return;\n        if (panel.parentElement === parent) return;\n\n        if (nextSibling && nextSibling.parentElement === parent) {\n          parent.insertBefore(panel, nextSibling);\n          return;\n        }\n\n        parent.appendChild(panel);\n      }\n\n      function syncHelpPanelPlacement() {\n        if (!elements.helpPanelMobileSlot) {\n          return;\n        }\n\n        const isMobileLayout = window.matchMedia(\"(max-width: 720px)\").matches;\n        const activePanel = getCurrentHelpPanel();\n\n        if (isMobileLayout) {\n          [elements.helpPanel, elements.rankedHelpPanel].forEach((panel) => {\n            if (!panel) return;\n\n            if (panel === activePanel) {\n              if (panel.parentElement !== elements.helpPanelMobileSlot) {\n                elements.helpPanelMobileSlot.appendChild(panel);\n              }\n              return;\n            }\n\n            if (panel === elements.helpPanel) {\n              restorePanelPlacement(panel, desktopHelpPanelParent, desktopHelpPanelNextSibling);\n              return;\n            }\n\n            restorePanelPlacement(panel, desktopRankedHelpPanelParent, desktopRankedHelpPanelNextSibling);\n          });\n          return;\n        }\n\n        restorePanelPlacement(elements.helpPanel, desktopHelpPanelParent, desktopHelpPanelNextSibling);\n        restorePanelPlacement(elements.rankedHelpPanel, desktopRankedHelpPanelParent, desktopRankedHelpPanelNextSibling);\n      }\n\n      function syncHelpPanelVisibility() {\n        const rankedActive = app.mode === \"ranked\";\n        elements.helpPanel.classList.toggle(\"hidden\", rankedActive);\n        elements.rankedHelpPanel.classList.toggle(\"hidden\", !rankedActive);\n      }\n\n      function setMessage(text, tone) {\n        if (standardMessageTimeoutId) {\n          window.clearTimeout(standardMessageTimeoutId);\n          standardMessageTimeoutId = 0;\n        }\n\n        elements.message.className = \"message\";\n        if (text && tone) {\n          elements.message.textContent = text;\n          elements.message.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\" && tone !== \"error-final\") {\n            standardMessageTimeoutId = window.setTimeout(function () {\n              elements.message.className = \"message\";\n              elements.message.textContent = \"\";\n              standardMessageTimeoutId = 0;\n            }, 3000);\n          }\n          return;\n        }\n\n        elements.message.textContent = \"\";\n      }\n\n      function setRankedMessage(text, tone) {\n        if (rankedMessageTimeoutId) {\n          window.clearTimeout(rankedMessageTimeoutId);\n          rankedMessageTimeoutId = 0;\n        }\n\n        elements.rankedMessage.className = \"message\";\n        if (text && tone) {\n          elements.rankedMessage.textContent = text;\n          elements.rankedMessage.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\") {\n            rankedMessageTimeoutId = window.setTimeout(function () {\n              elements.rankedMessage.className = \"message\";\n              elements.rankedMessage.textContent = \"\";\n              rankedMessageTimeoutId = 0;\n            }, 2200);\n          }\n          return;\n        }\n\n        elements.rankedMessage.textContent = \"\";\n      }\n\n      function renderBranding() {\n        const rankedActive = app.mode === \"ranked\";\n        const nextSrc = rankedActive ? RANKED_LOGO_URL : CLASSIC_LOGO_URL;\n        const nextAlt = rankedActive ? \"BulldogMatch Classificato\" : \"BulldogMatch\";\n\n        if (elements.topLogo.getAttribute(\"src\") !== nextSrc) {\n          elements.topLogo.setAttribute(\"src\", nextSrc);\n        }\n\n        elements.topLogo.setAttribute(\"alt\", nextAlt);\n        appRoot.classList.toggle(\"is-ranked-mode\", rankedActive);\n        document.body.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n        document.documentElement.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n      }\n\n      function renderTabs() {\n        elements.tabs.forEach((tab) => {\n          tab.classList.toggle(\"is-active\", tab.dataset.mode === app.mode);\n        });\n        elements.rankedModeButton.classList.toggle(\"is-active\", app.mode === \"ranked\");\n      }\n\n      function renderPanels() {\n        const rankedActive = app.mode === \"ranked\";\n        elements.standardGamePanel.classList.toggle(\"hidden\", rankedActive);\n        elements.rankedGamePanel.classList.toggle(\"hidden\", !rankedActive);\n        syncHelpPanelVisibility();\n        syncHelpPanelPlacement();\n      }\n\n      function renderStandardAttempts() {\n        const state = getCurrentStandardState();\n        const attemptsLeft = Math.max(0, state.maxAttempts - state.attempts);\n        elements.attemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderStandardActionSlot() {\n        if (app.mode === \"classic\") {\n          elements.modeActionButton.classList.add(\"hidden\");\n          elements.dailyStreakCard.classList.remove(\"hidden\");\n          loadDailyStreakStatus();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.add(\"hidden\");\n        elements.modeActionButton.className = \"secondary-button is-primary\";\n        elements.modeActionButton.classList.remove(\"hidden\");\n        elements.modeActionButton.textContent = \"NUOVO\";\n        elements.modeActionButton.disabled = false;\n      }\n\n      function renderStandardInputState() {\n        const state = getCurrentStandardState();\n        disableStandardInput(state.complete);\n        elements.inputLabel.textContent = \"inserisci un artista\";\n        if (app.mode === \"classic\" && state.complete) {\n          elements.input.placeholder = \"Torna domani!\";\n        } else if (app.mode === \"practice\" && state.complete) {\n          elements.input.placeholder = \"Premi NUOVO per ricominciare\";\n        } else {\n          elements.input.placeholder = \"inserisci un artista\";\n        }\n      }\n\n      function renderStandardResults() {\n        const state = getCurrentStandardState();\n        elements.resultsList.innerHTML = \"\";\n\n        if (!state.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = \"Nessun tentativo ancora.\";\n          elements.resultsList.appendChild(empty);\n          return;\n        }\n\n        state.results.forEach((result) => {\n          elements.resultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedMeta() {\n        elements.rankedTimerValue.textContent = formatTime(app.ranked.timeLeft);\n        elements.rankedScoreValue.textContent = String(app.ranked.score);\n        elements.rankedRoundValue.textContent = app.ranked.active\n          ? \"Artista #\" + String(app.ranked.score + 1)\n          : (app.ranked.complete ? \"Fine\" : \"Pronto\");\n      }\n\n      function renderRankedAttempts() {\n        const attemptsLeft = Math.max(0, RANKED_ATTEMPTS - app.ranked.roundAttempts);\n        elements.rankedAttemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderRankedInputState() {\n        disableRankedInput(!app.ranked.active);\n        elements.rankedInput.placeholder = app.ranked.active ? \"inserisci un artista\" : (app.ranked.complete ? \"Partita conclusa\" : \"inserisci un artista\");\n      }\n\n      function renderRankedActionButton() {\n        const button = elements.rankedSideActionButton;\n        button.className = \"secondary-button is-primary\";\n        button.disabled = false;\n\n        if (app.ranked.preparing || app.ranked.submitting) {\n          button.textContent = \"...\";\n          button.disabled = true;\n          button.dataset.action = \"wait\";\n          return;\n        }\n\n        if (app.ranked.active) {\n          if (app.ranked.betweenRounds) {\n            button.textContent = \"ATTENDI\";\n            button.disabled = true;\n            button.dataset.action = \"wait\";\n            return;\n          }\n          button.textContent = \"SKIP\";\n          button.dataset.action = \"skip\";\n          return;\n        }\n\n        if (app.ranked.complete) {\n          button.textContent = \"RIGIOCA\";\n          button.dataset.action = \"restart\";\n          return;\n        }\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          button.textContent = \"ACCEDI\";\n          button.dataset.action = \"login\";\n          return;\n        }\n\n        button.textContent = \"INIZIA\";\n        button.dataset.action = \"start\";\n      }\n\n      function renderRankedResults() {\n        elements.rankedResultsList.innerHTML = \"\";\n\n        if (!app.ranked.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = app.ranked.complete\n            ? \"La partita e finita. Puoi controllare il riepilogo finale.\"\n            : (app.ranked.active\n              ? \"I tuoi tentativi appariranno qui ordinati per affinit\u00e0.\"\n              : (hasRankedBackend && !rankedConfig.isLoggedIn ? \"ACCEDI per giocare.\" : \"Premi INIZIA per giocare.\"));\n          elements.rankedResultsList.appendChild(empty);\n          return;\n        }\n\n        app.ranked.results.forEach((result) => {\n          elements.rankedResultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedSummary() {\n        elements.rankedSummaryPanel.classList.toggle(\"hidden\", !app.ranked.complete || !app.ranked.summaryLines.length);\n        elements.rankedSummaryPanel.classList.toggle(\"is-record\", app.ranked.isRecord);\n        elements.rankedSummaryTitle.textContent = app.ranked.summaryTitle || \"Partita conclusa\";\n        elements.rankedSummaryLines.innerHTML = \"\";\n\n        app.ranked.summaryLines.forEach((line) => {\n          const row = document.createElement(\"div\");\n          row.className = \"ranked-summary-line\";\n          row.innerHTML = line;\n          elements.rankedSummaryLines.appendChild(row);\n        });\n      }\n\n      function renderRankedPanel() {\n        renderRankedMeta();\n        renderRankedAttempts();\n        renderRankedInputState();\n        renderRankedActionButton();\n        renderRankedSummary();\n        renderRankedResults();\n        hideRankedSuggestions();\n      }\n\n      function renderApp() {\n        renderBranding();\n        renderTabs();\n        renderPanels();\n\n        if (app.mode === \"ranked\") {\n          renderRankedPanel();\n          hideSuggestions();\n          syncHelpToggleState();\n          return;\n        }\n\n        renderStandardAttempts();\n        renderStandardActionSlot();\n        renderStandardInputState();\n        renderStandardResults();\n        hideSuggestions();\n        hideRankedSuggestions();\n        syncHelpToggleState();\n      }\n\n      function formatTime(totalSeconds) {\n        const safeTime = Math.max(0, totalSeconds);\n        const minutes = Math.floor(safeTime \/ 60);\n        const seconds = String(safeTime % 60).padStart(2, \"0\");\n        return minutes + \":\" + seconds;\n      }\n\n      function getClassicSaveStatus() {\n        const state = app.modes.classic;\n        if (!state.complete) return \"playing\";\n        return state.results.some((result) => result.kind === \"guess\" && result.solved) ? \"won\" : \"lost\";\n      }\n\n      function saveClassicState(status) {\n        const state = app.modes.classic;\n        const payload = {\n          attempts: state.attempts,\n          guessedIds: Array.from(state.guessedIds),\n          guessedArtistIds: state.results.filter((result) => result.kind === \"guess\").map((result) => result.artist.id),\n          status: status || getClassicSaveStatus()\n        };\n\n        try {\n          window.localStorage.setItem(dailyStorageKey, JSON.stringify(payload));\n        } catch (_) {}\n      }\n\n      function loadClassicState() {\n        let rawState = \"\";\n\n        try {\n          rawState = window.localStorage.getItem(dailyStorageKey) || \"\";\n        } catch (_) {\n          return;\n        }\n\n        if (!rawState) return;\n\n        try {\n          const savedState = JSON.parse(rawState);\n          const state = app.modes.classic;\n          const savedArtistIds = Array.isArray(savedState.guessedArtistIds)\n            ? savedState.guessedArtistIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n            : [];\n          const restoredGuessedIds = new Set(\n            Array.isArray(savedState.guessedIds)\n              ? savedState.guessedIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n              : []\n          );\n\n          state.results = [];\n          state.guessedIds = restoredGuessedIds;\n          state.attempts = Math.max(0, Math.min(state.maxAttempts, Number(savedState.attempts || savedArtistIds.length)));\n          state.complete = false;\n\n          savedArtistIds.forEach((id, index) => {\n            const artist = artists.find((entry) => entry.id === id);\n            if (!artist) return;\n\n            state.guessedIds.add(artist.id);\n            state.results.push(buildResultEntry(artist, state.target, {\n              kind: \"guess\",\n              label: \"Tentativo \" + (index + 1)\n            }));\n          });\n\n          state.results.sort((left, right) => right.affinity - left.affinity);\n\n          if (savedState.status === \"won\") {\n            state.complete = true;\n            setMessage(\"Hai indovinato \" + state.target.name + \".\", \"success\");\n            syncDailyStreakResult(\"won\");\n            return;\n          }\n\n          if (savedState.status === \"lost\") {\n            state.complete = true;\n            state.results.unshift(buildResultEntry(state.target, state.target, {\n              kind: \"answer\",\n              label: \"Artista corretto\"\n            }));\n            setMessage(\"Tentativi finiti. L'artista era \" + state.target.name + \".\", \"error-final\");\n            syncDailyStreakResult(\"lost\");\n            return;\n          }\n\n          setMessage(\"\", \"\");\n        } catch (error) {\n          console.error(\"Errore nel caricamento del Daily Match salvato:\", error);\n          try {\n            window.localStorage.removeItem(dailyStorageKey);\n          } catch (_) {}\n        }\n      }\n\n      function addStandardGuess(guessArtist, modeKey) {\n        const state = app.modes[modeKey];\n        const targetArtist = state.target;\n        const attemptNumber = state.attempts + 1;\n        const isSolved = guessArtist.id === targetArtist.id;\n\n        state.guessedIds.add(guessArtist.id);\n        state.attempts += 1;\n        state.results.push(buildResultEntry(guessArtist, targetArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + attemptNumber\n        }));\n        state.results.sort((left, right) => right.affinity - left.affinity);\n\n        if (isSolved) {\n          state.complete = true;\n          setMessage(\"Hai indovinato \" + targetArtist.name + \".\", \"success\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"won\");\n            syncDailyStreakResult(\"won\");\n          }\n          return;\n        }\n\n        if (state.attempts >= state.maxAttempts) {\n          state.complete = true;\n          state.results.unshift(buildResultEntry(targetArtist, targetArtist, {\n            kind: \"answer\",\n            label: \"Artista corretto\"\n          }));\n          setMessage(\"Tentativi finiti. L'artista era \" + targetArtist.name + \".\", \"error-final\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"lost\");\n            syncDailyStreakResult(\"lost\");\n          }\n          return;\n        }\n\n        setMessage(\"\", \"\");\n        if (modeKey === \"classic\") {\n          saveClassicState(\"playing\");\n        }\n      }\n\n      function submitStandardGuess(modeKey) {\n        const state = app.modes[modeKey];\n        if (state.complete) return;\n\n        const guessArtist = findArtistByName(elements.input.value.trim());\n        if (!guessArtist) {\n          setMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (state.guessedIds.has(guessArtist.id)) {\n          setMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.input.value = \"\";\n          return;\n        }\n\n        addStandardGuess(guessArtist, modeKey);\n        elements.input.value = \"\";\n      }\n\n      function startPracticeRound() {\n        const nextRound = (app.modes.practice.round || 0) + 1;\n        app.modes.practice = {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: nextRound,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n        if (app.mode === \"practice\") {\n          setMessage(\"\", \"\");\n          renderApp();\n        }\n      }\n\n      function renderDailyStreakGuestState() {\n        elements.dailyStreakCard.classList.add(\"is-guest\");\n        elements.dailyStreakCard.setAttribute(\"href\", guestLoginUrl);\n        elements.dailyStreakCard.setAttribute(\"aria-label\", \"Accedi per salvare la streak\");\n        elements.dailyStreakMain.textContent = \"STREAK\";\n        elements.dailyStreakSub.textContent = \"Accedi per salvarla\";\n      }\n\n      function renderDailyStreakLoadingState() {\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        elements.dailyStreakMain.textContent = \"...\";\n        elements.dailyStreakSub.textContent = \"Caricamento streak\";\n      }\n\n      function renderDailyStreakState(data) {\n        if (!data || !data.is_logged_in) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        const streakValue = Number(data.current_streak || 0);\n        elements.dailyStreakMain.textContent = streakValue + \" \" + (streakValue === 1 ? \"giorno\" : \"giorni\");\n\n        if (data.today_result === \"won\") {\n          elements.dailyStreakSub.textContent = \"Indovinata oggi \u2022 Record \" + (data.best_streak || data.current_streak || 0);\n        } else if (data.today_result === \"lost\") {\n          elements.dailyStreakSub.textContent = \"Interrotta oggi \u2022 Record \" + (data.best_streak || 0);\n        } else {\n          elements.dailyStreakSub.textContent = \"Record \" + (data.best_streak || data.current_streak || 0);\n        }\n      }\n\n      async function postDailyStreakAction(action, extraData) {\n        const ajaxUrl = matchConfig.ajaxUrl;\n        if (!ajaxUrl) {\n          throw new Error(\"Config streak Match non trovata\");\n        }\n\n        const body = new URLSearchParams({\n          action: action,\n          nonce: matchConfig.nonce || \"\",\n          ...(extraData || {})\n        });\n\n        const response = await fetch(ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta streak Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      async function loadDailyStreakStatus(force) {\n        if (!matchConfig.isLoggedIn) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        if (dailyStreakLoading) return;\n        if (dailyStreakLoaded && !force) return;\n\n        dailyStreakLoading = true;\n        renderDailyStreakLoadingState();\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_status\");\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n            dailyStreakLoaded = true;\n          } else {\n            renderDailyStreakGuestState();\n          }\n        } catch (_) {\n          renderDailyStreakGuestState();\n        } finally {\n          dailyStreakLoading = false;\n        }\n      }\n\n      async function syncDailyStreakResult(result) {\n        if (!matchConfig.isLoggedIn) return;\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_update\", {\n            result: result,\n            day_key: italyDateInfo.dayKey\n          });\n\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n          }\n        } catch (error) {\n          console.error(\"Errore aggiornamento streak Bulldog Match:\", error);\n        }\n      }\n\n      async function postRankedAction(action, extraData) {\n        const body = new URLSearchParams({\n          action: action,\n          nonce: rankedConfig.nonce || \"\",\n          game_key: RANKED_GAME_KEY,\n          ...(extraData || {})\n        });\n\n        const response = await fetch(rankedConfig.ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta ranked Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      function getRankedLoginUrl() {\n        return rankedConfig.loginUrl || guestLoginUrl;\n      }\n\n      function resetRankedState() {\n        const state = app.ranked;\n        state.active = false;\n        state.preparing = false;\n        state.submitting = false;\n        state.complete = false;\n        state.betweenRounds = false;\n        state.sessionToken = \"\";\n        state.currentArtist = null;\n        state.score = 0;\n        state.timeLeft = RANKED_INITIAL_TIME;\n        state.roundAttempts = 0;\n        state.roundGuessedIds = new Set();\n        state.usedTargetIds = new Set();\n        state.freeHintArtist = null;\n        state.results = [];\n        state.summaryTitle = \"Partita conclusa\";\n        state.summaryLines = [];\n        state.isRecord = false;\n        state.localBest = readLocalRankedBest();\n        window.clearInterval(state.timerId);\n        window.clearTimeout(state.roundTransitionId);\n        state.timerId = 0;\n        state.roundTransitionId = 0;\n        setRankedMessage(\"\", \"\");\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n      }\n\n      function startNextRankedArtist() {\n        if (!app.ranked.active) return;\n\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.betweenRounds = false;\n\n        if (app.ranked.usedTargetIds.size >= artists.length) {\n          app.ranked.usedTargetIds.clear();\n        }\n\n        const nextArtist = pickRandomArtist(app.ranked.usedTargetIds);\n        app.ranked.currentArtist = nextArtist;\n        app.ranked.usedTargetIds.add(nextArtist.id);\n        app.ranked.roundAttempts = 0;\n        app.ranked.roundGuessedIds = new Set();\n        app.ranked.results = [];\n\n        if (app.ranked.freeHintArtist) {\n          app.ranked.results.unshift(buildResultEntry(app.ranked.freeHintArtist, app.ranked.currentArtist, {\n            kind: \"auto\",\n            label: \"Reveal artista passato\",\n            subcopy: \"Indizio gratis dal round precedente\",\n            ranked: true\n          }));\n          app.ranked.freeHintArtist = null;\n        }\n\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedPanel();\n        if (app.mode === \"ranked\") {\n          elements.rankedInput.focus();\n        }\n      }\n\n      function completeRankedRound(message, tone, carryArtist) {\n        if (!app.ranked.active || !app.ranked.currentArtist) return;\n        if (carryArtist) app.ranked.freeHintArtist = app.ranked.currentArtist;\n\n        app.ranked.betweenRounds = true;\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        renderRankedInputState();\n        renderRankedActionButton();\n        setRankedMessage(message, tone);\n\n        app.ranked.roundTransitionId = window.setTimeout(function () {\n          if (!app.ranked.active || app.ranked.timeLeft <= 0) return;\n          startNextRankedArtist();\n        }, RANKED_ROUND_TRANSITION_MS);\n      }\n\n      function tickRanked() {\n        if (!app.ranked.active) return;\n        app.ranked.timeLeft -= 1;\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n        }\n      }\n\n      async function startRankedGame() {\n        if (app.ranked.active || app.ranked.preparing) return;\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        app.ranked.preparing = true;\n        app.ranked.complete = false;\n        app.ranked.summaryLines = [];\n        renderRankedActionButton();\n\n        try {\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_start\");\n            if (!response || !response.success || !response.data || !response.data.is_logged_in) {\n              window.location.href = getRankedLoginUrl();\n              return;\n            }\n            app.ranked.sessionToken = String(response.data.token || \"\");\n          } else {\n            app.ranked.sessionToken = \"demo-\" + String(Date.now());\n          }\n\n          window.clearInterval(app.ranked.timerId);\n          window.clearTimeout(app.ranked.roundTransitionId);\n          app.ranked.active = true;\n          app.ranked.complete = false;\n          app.ranked.betweenRounds = false;\n          app.ranked.submitting = false;\n          app.ranked.score = 0;\n          app.ranked.timeLeft = RANKED_INITIAL_TIME;\n          app.ranked.roundAttempts = 0;\n          app.ranked.roundGuessedIds = new Set();\n          app.ranked.usedTargetIds = new Set();\n          app.ranked.freeHintArtist = null;\n          app.ranked.results = [];\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = [];\n          app.ranked.isRecord = false;\n          setRankedMessage(\"\", \"\");\n          renderRankedPanel();\n          startNextRankedArtist();\n          app.ranked.timerId = window.setInterval(tickRanked, 1000);\n        } catch (_) {\n          setRankedMessage(\"Non sono riuscito ad avviare la partita. Riprova tra poco.\", \"error\");\n        } finally {\n          app.ranked.preparing = false;\n          renderRankedInputState();\n          renderRankedActionButton();\n        }\n      }\n\n      function submitRankedGuess() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n\n        const guessArtist = findArtistByName(elements.rankedInput.value.trim());\n        if (!guessArtist) {\n          setRankedMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (app.ranked.roundGuessedIds.has(guessArtist.id)) {\n          setRankedMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.rankedInput.value = \"\";\n          return;\n        }\n\n        app.ranked.roundGuessedIds.add(guessArtist.id);\n        app.ranked.roundAttempts += 1;\n        app.ranked.results.push(buildResultEntry(guessArtist, app.ranked.currentArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + app.ranked.roundAttempts,\n          ranked: true\n        }));\n        app.ranked.results.sort((left, right) => right.affinity - left.affinity);\n        renderRankedAttempts();\n        renderRankedResults();\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n\n        if (guessArtist.id === app.ranked.currentArtist.id) {\n          app.ranked.score += 1;\n          app.ranked.timeLeft += RANKED_CORRECT_BONUS;\n          renderRankedMeta();\n          completeRankedRound(\"Hai indovinato!\", \"success\", true);\n          return;\n        }\n\n        if (app.ranked.roundAttempts >= RANKED_ATTEMPTS) {\n          completeRankedRound(\"Hai finito i tentativi! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n        }\n      }\n\n      function skipRankedArtist() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n        app.ranked.timeLeft = Math.max(0, app.ranked.timeLeft - RANKED_SKIP_PENALTY);\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n          return;\n        }\n        completeRankedRound(\"Hai skippato! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n      }\n\n      function escapeHtml(value) {\n        const div = document.createElement(\"div\");\n        div.textContent = value || \"\";\n        return div.innerHTML;\n      }\n\n      function buildRankedSummaryLines(data) {\n        const lines = [];\n        if (app.ranked.currentArtist && app.ranked.currentArtist.name) {\n          lines.push(\"L'ultimo artista era: <strong>\" + escapeHtml(app.ranked.currentArtist.name) + \"<\/strong>\");\n        }\n        lines.push(\"Punteggio finale: <strong>\" + String(app.ranked.score) + \"<\/strong>\");\n\n        if (data && typeof data.best_score !== \"undefined\") {\n          lines.push(\"Record mensile: <strong>\" + String(data.best_score) + \"<\/strong>\");\n          lines.push(\"Migliore posizione: <strong>\" + (data.best_rank ? \"#\" + data.best_rank : \"Fuori top 50\") + \"<\/strong>\");\n        } else {\n          const localBest = writeLocalRankedBest(app.ranked.score);\n          lines.push(\"Record demo: <strong>\" + String(localBest) + \"<\/strong>\");\n        }\n\n        return lines;\n      }\n\n      async function finishRankedGame() {\n        if (!app.ranked.active || app.ranked.submitting) return;\n\n        app.ranked.active = false;\n        app.ranked.complete = true;\n        app.ranked.betweenRounds = false;\n        app.ranked.submitting = true;\n        app.ranked.timeLeft = 0;\n        window.clearInterval(app.ranked.timerId);\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.timerId = 0;\n        app.ranked.roundTransitionId = 0;\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedMeta();\n        renderRankedInputState();\n        renderRankedActionButton();\n        app.ranked.summaryTitle = \"Partita conclusa\";\n        app.ranked.summaryLines = [\"Salvataggio record...\"];\n        app.ranked.isRecord = false;\n        renderRankedSummary();\n\n        try {\n          let data = null;\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_submit\", {\n              token: app.ranked.sessionToken,\n              score: String(app.ranked.score)\n            });\n            data = response && response.success ? response.data : null;\n          }\n\n          if (data) {\n            app.ranked.summaryTitle = data.is_record ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(data);\n            app.ranked.isRecord = Boolean(data.is_record);\n          } else {\n            const previousBest = readLocalRankedBest();\n            const nextBest = Math.max(previousBest, app.ranked.score);\n            writeLocalRankedBest(nextBest);\n            app.ranked.summaryTitle = nextBest === app.ranked.score && app.ranked.score > previousBest ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(null);\n            app.ranked.isRecord = app.ranked.score > previousBest;\n          }\n        } catch (_) {\n          const previousBest = readLocalRankedBest();\n          const nextBest = Math.max(previousBest, app.ranked.score);\n          writeLocalRankedBest(nextBest);\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = buildRankedSummaryLines(null);\n          app.ranked.isRecord = app.ranked.score > previousBest;\n        } finally {\n          app.ranked.submitting = false;\n          renderRankedPanel();\n        }\n      }\n\n      function buildSuggestionList(matches) {\n        elements.suggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.input.value = artist.name;\n            hideSuggestions();\n            elements.input.focus();\n          });\n          elements.suggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showSuggestions();\n          return;\n        }\n\n        hideSuggestions();\n      }\n\n      function updateSuggestions() {\n        const value = normalize(elements.input.value);\n        if (!value || getCurrentStandardState().complete) {\n          hideSuggestions();\n          return;\n        }\n\n        const guessedIds = getCurrentStandardState().guessedIds;\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !guessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        buildSuggestionList(matches);\n      }\n\n      function updateRankedSuggestions() {\n        const value = normalize(elements.rankedInput.value);\n        if (!value || !app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) {\n          hideRankedSuggestions();\n          return;\n        }\n\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !app.ranked.roundGuessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        elements.rankedSuggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.rankedInput.value = artist.name;\n            hideRankedSuggestions();\n            elements.rankedInput.focus();\n          });\n          elements.rankedSuggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showRankedSuggestions();\n          return;\n        }\n\n        hideRankedSuggestions();\n      }\n\n      function handleRankedSideAction() {\n        const action = elements.rankedSideActionButton.dataset.action;\n\n        if (action === \"login\") {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        if (action === \"start\" || action === \"restart\") {\n          startRankedGame();\n          return;\n        }\n\n        if (action === \"skip\") {\n          skipRankedArtist();\n        }\n      }\n\n      function switchMode(nextMode) {\n        if (nextMode === app.mode) return;\n\n        if (app.mode === \"ranked\" && nextMode !== \"ranked\") {\n          resetRankedState();\n        }\n\n        app.mode = nextMode;\n        elements.input.value = \"\";\n        elements.rankedInput.value = \"\";\n        hideSuggestions();\n        hideRankedSuggestions();\n        closeAllHelpPanels();\n        setMessage(\"\", \"\");\n        setRankedMessage(\"\", \"\");\n        renderApp();\n      }\n\n      elements.tabs.forEach((tab) => {\n        tab.addEventListener(\"click\", function () {\n          switchMode(tab.dataset.mode);\n        });\n      });\n\n      elements.rankedModeButton.addEventListener(\"click\", function () {\n        switchMode(\"ranked\");\n      });\n\n      elements.helpToggle.addEventListener(\"click\", function () {\n        const panel = getCurrentHelpPanel();\n        if (!panel) return;\n\n        const shouldOpen = !panel.classList.contains(\"is-open\");\n        closeAllHelpPanels();\n\n        if (shouldOpen) {\n          panel.classList.add(\"is-open\");\n          panel.setAttribute(\"aria-hidden\", \"false\");\n        }\n\n        syncHelpToggleState();\n      });\n\n      elements.dailyStreakCard.addEventListener(\"click\", function (event) {\n        if (elements.dailyStreakCard.classList.contains(\"is-guest\")) {\n          event.preventDefault();\n          window.location.href = elements.dailyStreakCard.getAttribute(\"href\") || guestLoginUrl;\n          return;\n        }\n\n        event.preventDefault();\n        event.stopPropagation();\n      });\n\n      elements.modeActionButton.addEventListener(\"click\", function () {\n        if (app.mode === \"practice\") {\n          startPracticeRound();\n        }\n      });\n\n      elements.guessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitStandardGuess(app.mode === \"classic\" ? \"classic\" : \"practice\");\n        renderApp();\n      });\n\n      elements.rankedGuessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitRankedGuess();\n        renderRankedPanel();\n      });\n\n      elements.rankedSideActionButton.addEventListener(\"click\", function () {\n        handleRankedSideAction();\n      });\n\n      elements.input.addEventListener(\"input\", updateSuggestions);\n      elements.input.addEventListener(\"focus\", updateSuggestions);\n      elements.rankedInput.addEventListener(\"input\", updateRankedSuggestions);\n      elements.rankedInput.addEventListener(\"focus\", updateRankedSuggestions);\n\n      document.addEventListener(\"click\", function (event) {\n        if (!elements.suggestions.contains(event.target) && event.target !== elements.input) {\n          hideSuggestions();\n        }\n\n        if (!elements.rankedSuggestions.contains(event.target) && event.target !== elements.rankedInput) {\n          hideRankedSuggestions();\n        }\n      });\n\n      window.addEventListener(\"resize\", function () {\n        syncHelpPanelPlacement();\n\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      });\n\n      window.addEventListener(\"scroll\", function () {\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      }, true);\n\n      if (window.visualViewport) {\n        window.visualViewport.addEventListener(\"resize\", function () {\n          syncHelpPanelPlacement();\n\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n\n        window.visualViewport.addEventListener(\"scroll\", function () {\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n      }\n\n      loadClassicState();\n      renderApp();\n      if (!app.modes.classic.complete) {\n        setMessage(\"\", \"\");\n      }\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2b8a8bcd crew elementor-widget__width-initial e-transform elementor-widget elementor-widget-post-widget\" data-id=\"2b8a8bcd\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;posts_per_page&quot;:100,&quot;post_type&quot;:[&quot;page&quot;],&quot;nb_columns&quot;:&quot;1&quot;,&quot;_transform_translateX_effect&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:4,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_tablet&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_mobile&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:3,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_tablet&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_mobile&quot;:{&quot;unit&quot;:&quot;%&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;classic_layout&quot;:&quot;grid&quot;,&quot;nb_columns_tablet&quot;:&quot;3&quot;,&quot;nb_columns_mobile&quot;:&quot;1&quot;,&quot;post_status&quot;:&quot;publish&quot;,&quot;pagination&quot;:&quot;none&quot;}\" data-widget_type=\"post-widget.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"post-container none classic\" data-total-post=\"2\" ><div class=\"post-container-inner\"><div class=\"elementor-grid\" ><article class=\"post-wrapper\"  data-post-id=\"8846\" ><div class=\"post-image\"><a href=\"https:\/\/bulldogmusik.it\/en\/bulldogfeat-ita\/\" ><img loading=\"lazy\" decoding=\"async\" width=\"1366\" height=\"768\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogFeat-Logo-1.png\" class=\"bpfwe-post-thumbnail\" alt=\"\" data-bpfwe-src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogFeat-Logo-1.png\" loading=\"lazy\" \/><\/a><\/div><div class=\"inner-content\"><div class=\"post-content elementor-repeater-item-aff2fa8\"><p>Indovina in rapper in meno di 5 tentativi, ogni tentativo scopri altre collaborazioni....<\/p><\/div><\/div><\/article><article class=\"post-wrapper\"  data-post-id=\"7057\" ><div class=\"post-image\"><a href=\"https:\/\/bulldogmusik.it\/en\/bulldogle-rap-ita\/\" ><img loading=\"lazy\" decoding=\"async\" width=\"1366\" height=\"768\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Bulldogle-Classico-New.png\" class=\"bpfwe-post-thumbnail\" alt=\"\" data-bpfwe-src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Bulldogle-Classico-New.png\" loading=\"lazy\" \/><\/a><\/div><div class=\"inner-content\"><div class=\"post-content elementor-repeater-item-aff2fa8\"><p>Indovina il rapper in meno di 10 tentativi, ogni tentativo scopri nuove informazioni...<\/p><\/div><\/div><\/article><\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div id=\"e-n-tab-content-7596930762\" role=\"tabpanel\" aria-labelledby=\"tab-speciale\" data-tab-index=\"2\" style=\"--n-tabs-title-order: 2;\" class=\" elementor-element elementor-element-1d71dd2d e-con-full e-flex wcf-starter-animations-none e-con e-child\" data-id=\"1d71dd2d\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-6c075c5f e-grid e-con-boxed wcf-starter-animations-none e-con e-child\" data-id=\"6c075c5f\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-242227fa elementor-widget__width-initial e-transform elementor-widget elementor-widget-html\" data-id=\"242227fa\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_transform_translateX_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:-4,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateX_effect_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_tablet&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_translateY_effect_mobile&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"it\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>BulldogMatch Ranked Demo<\/title>\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Instrument+Sans:wght@400;500;600;700;800&family=Space+Grotesk:wght@500;700&display=swap\" rel=\"stylesheet\">\n  <style>\n    :root {\n      --bg: #000;\n      --panel: #070707;\n      --panel-2: #0b0b0b;\n      --line: rgba(255, 255, 255, 0.08);\n      --line-faint: rgba(255, 255, 255, 0.05);\n      --line-strong: rgba(241, 108, 29, 0.28);\n      --ranked-line-strong: rgba(149, 40, 0, 0.28);\n      --white: #f7f4ef;\n      --copy: rgba(247, 244, 239, 0.76);\n      --copy-soft: rgba(247, 244, 239, 0.56);\n      --copy-faint: rgba(247, 244, 239, 0.34);\n      --accent: #f16c1d;\n      --ranked-accent: #952800;\n      --success: #7dc56a;\n      --warning: #f0b955;\n      --danger: #ff7e59;\n      --ease: 220ms cubic-bezier(.2,.9,.2,1);\n    }\n\n    * {\n      box-sizing: border-box;\n    }\n\n    html,\n    body {\n      margin: 0;\n      min-height: 100%;\n      background: var(--bg);\n      color: var(--white);\n      font-family: \"Instrument Sans\", system-ui, sans-serif;\n      overflow-x: hidden;\n    }\n\n    button,\n    input,\n    iframe {\n      font: inherit;\n    }\n\n    .demo-app {\n      width: min(760px, calc(100vw - 20px));\n      margin: 0 auto;\n      padding: 18px 0 24px;\n    }\n\n    .app-shell {\n      position: relative;\n      isolation: isolate;\n      padding: 14px;\n      border: 1px solid var(--line);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.98), rgba(7, 7, 7, 0.98));\n      overflow: visible;\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n    }\n\n    .app-shell::after {\n      content: \"\";\n      position: absolute;\n      inset: 0;\n      border-radius: inherit;\n      background-image: url(\"data:image\/svg+xml,%3Csvg%20xmlns='http:\/\/www.w3.org\/2000\/svg'%20viewBox='0%200%20180%20180'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='.9'%20numOctaves='3'%20stitchTiles='stitch'\/%3E%3CfeColorMatrix%20type='saturate'%20values='0'\/%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type='table'%20tableValues='0%20.58'\/%3E%3C\/feComponentTransfer%3E%3C\/filter%3E%3Crect%20width='180'%20height='180'%20filter='url(%23n)'%20opacity='1'\/%3E%3C\/svg%3E\");\n      background-size: 180px 180px;\n      mix-blend-mode: overlay;\n      opacity: 0.1;\n      pointer-events: none;\n    }\n\n    .app-shell > * {\n      position: relative;\n      z-index: 1;\n    }\n\n    .topbar {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 0;\n      padding: 10px 8px 14px;\n    }\n\n    .top-logo {\n      display: block;\n      width: min(320px, 58vw);\n      height: auto;\n    }\n\n    .ranked-demo-app .controls-shell {\n      display: none;\n      padding: 14px 0;\n      border-top: 1px solid var(--line);\n    }\n\n    .ranked-demo-app .controls-inner {\n      display: grid;\n      grid-template-columns: repeat(2, minmax(0, 1fr));\n      gap: 8px;\n    }\n\n    .shell {\n      margin-top: 0;\n      padding-top: 14px;\n      border-top: 1px solid var(--line);\n    }\n\n    .game-shell {\n      position: relative;\n      padding: 2px 0 6px;\n      overflow: visible;\n    }\n\n    .tab,\n    .ghost-button,\n    .button,\n    .secondary-button {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-width: 0;\n      min-height: 36px;\n      padding: 0 10px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--white);\n      font-size: 12px;\n      font-weight: 700;\n      cursor: pointer;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .tab.is-active,\n    .ghost-button.is-active {\n      border-color: var(--line-strong);\n      background: rgba(241, 108, 29, 0.12);\n    }\n\n    .demo-app.is-ranked-mode #rankedModeButton.is-active,\n    .demo-app.is-ranked-mode .ghost-button.is-active {\n      border-color: var(--ranked-line-strong);\n      background: rgba(149, 40, 0, 0.12);\n    }\n\n    .demo-app.is-ranked-mode .hide-when-ranked,\n    body.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    html.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    .demo-app.is-ranked-mode .hide.when.ranked,\n    body.bulldogmatch-ranked-mode-active .hide.when.ranked,\n    html.bulldogmatch-ranked-mode-active .hide.when.ranked {\n      display: none !important;\n    }\n\n    .show-when-ranked,\n    .show.when.ranked,\n    .show-when-ranked-flex,\n    .show.flex.when.ranked,\n    .show-when-ranked-grid,\n    .show.grid.when.ranked,\n    .show-when-ranked-inline {\n      display: none !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked,\n    .demo-app.is-ranked-mode .show.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.when.ranked {\n      display: block !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-flex,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    .demo-app.is-ranked-mode .show.flex.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.flex.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.flex.when.ranked {\n      display: flex !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-grid,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    .demo-app.is-ranked-mode .show.grid.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.grid.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.grid.when.ranked {\n      display: grid !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-inline,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-inline,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-inline {\n      display: inline-flex !important;\n    }\n\n    .button,\n    .secondary-button,\n    .attempts-card {\n      min-width: 152px;\n      min-height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n      font-variation-settings: \"wght\" 700;\n      box-shadow: none;\n    }\n\n    .button,\n    .secondary-button.is-primary {\n      background: var(--accent);\n      border-color: var(--accent);\n      color: #111;\n    }\n\n    .secondary-button.is-muted {\n      background: rgba(255, 255, 255, 0.03);\n      border-color: var(--line);\n      color: var(--white);\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-form .button,\n    .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary {\n      background: var(--ranked-accent);\n      border-color: var(--ranked-accent);\n      color: #111;\n    }\n\n    .button:hover,\n    .secondary-button:hover {\n      background: #fff;\n      border-color: #fff;\n      color: #111;\n    }\n\n    .tab:hover,\n    .ghost-button:hover,\n    .button:hover,\n    .secondary-button:hover,\n    .streak-card.is-guest:hover,\n    .suggestion:hover {\n      transform: translateY(-1px);\n    }\n\n    .button:disabled,\n    .secondary-button:disabled {\n      cursor: not-allowed;\n      opacity: 0.45;\n      transform: none;\n    }\n\n    .help-host {\n      margin-top: 10px;\n    }\n\n    .help-panel {\n      max-height: 0;\n      overflow: hidden;\n      padding-bottom: 0;\n      border-bottom: 1px solid var(--line);\n      transition: max-height 240ms ease, margin 240ms ease, padding-bottom 240ms ease, border-color 240ms ease;\n      margin: 0 0 12px;\n    }\n\n    .help-panel.is-open {\n      max-height: 1200px;\n      padding-bottom: 12px;\n      margin: 0 0 12px;\n    }\n\n    .help-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n      gap: 10px;\n    }\n\n    .help-card {\n      min-height: 100%;\n      padding: 12px 13px;\n      border: 1px solid var(--line);\n      border-radius: 12px;\n      background: rgba(255, 255, 255, 0.03);\n    }\n\n    .help-card strong {\n      display: block;\n      margin-bottom: 8px;\n      font-size: 13px;\n    }\n\n    .help-card span {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-standard-panel,\n    .match-ranked-panel {\n      display: grid;\n      gap: 8px;\n    }\n\n    .match-ranked-panel.hidden,\n    .match-standard-panel.hidden,\n    .help-panel.hidden,\n    .hidden {\n      display: none !important;\n    }\n\n    .match-meta-grid {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 10px;\n      margin-bottom: 12px;\n    }\n\n    @media (min-width: 721px) {\n      .ranked-demo-app .match-meta-grid {\n        padding-bottom: 14px;\n        border-bottom: 1px solid var(--line);\n      }\n\n      .ranked-demo-app #rankedHelpPanelInline {\n        border-bottom-color: transparent;\n      }\n\n      .ranked-demo-app #rankedHelpPanelInline.is-open {\n        border-bottom-color: var(--line);\n      }\n    }\n\n    .match-meta-card {\n      min-height: 74px;\n      padding: 12px 14px;\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      background: rgba(255, 255, 255, 0.03);\n      display: flex;\n      flex-direction: column;\n      justify-content: space-between;\n      gap: 8px;\n      text-align: left;\n    }\n\n    .match-meta-card.is-accent {\n      border-color: rgba(149, 40, 0, 0.24);\n      background: rgba(149, 40, 0, 0.08);\n    }\n\n    .ranked-meta-button {\n      width: 100%;\n      min-height: 74px;\n      padding: 12px 14px;\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--white);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      text-align: center;\n      font-size: 14px;\n      font-weight: 700;\n      cursor: pointer;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease);\n    }\n\n    .match-meta-grid > .match-meta-card,\n    .match-meta-grid > .ranked-meta-button {\n      align-self: stretch;\n      height: auto;\n    }\n\n    .ranked-meta-button.is-active {\n      border-color: var(--ranked-line-strong);\n      background: rgba(149, 40, 0, 0.12);\n    }\n\n    .ranked-meta-button:hover {\n      transform: translateY(-1px);\n    }\n\n    .match-meta-label {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.12em;\n      text-transform: uppercase;\n    }\n\n    .match-meta-value {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(24px, 3.2vw, 30px);\n      line-height: 1;\n      letter-spacing: -0.05em;\n    }\n\n    .input-grid {\n      display: grid;\n      grid-template-columns: 152px minmax(0, 1fr) 152px 152px;\n      gap: 10px;\n      align-items: stretch;\n      width: 100%;\n      margin: 0 auto;\n    }\n\n    .attempts-card {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      padding: 0 22px;\n      border: 1px solid #fff;\n      border-radius: 12px;\n      background: #fff;\n      color: #111;\n      text-align: center;\n      white-space: nowrap;\n    }\n\n    .action-slot {\n      width: 152px;\n      min-width: 152px;\n      align-self: stretch;\n    }\n\n    .input-grid > .attempts-card,\n    .input-grid > .button,\n    .action-slot > .secondary-button,\n    .action-slot > .streak-card {\n      width: 100%;\n      min-width: 0;\n      min-height: 56px;\n      height: 56px;\n    }\n\n    .input-grid > .action-slot {\n      align-self: stretch;\n      height: 56px;\n    }\n\n    .input-shell {\n      position: relative;\n      z-index: 5;\n      min-width: 0;\n    }\n\n    .input-label {\n      position: absolute;\n      width: 1px;\n      height: 1px;\n      padding: 0;\n      margin: -1px;\n      overflow: hidden;\n      clip: rect(0, 0, 0, 0);\n      white-space: nowrap;\n      border: 0;\n    }\n\n    .search-field {\n      width: 100%;\n      min-height: 56px;\n      padding: 0 16px;\n      border-radius: 12px;\n      border: 1px solid rgba(255, 255, 255, 0.1);\n      background: #101010;\n      color: var(--white);\n      font-size: 15px;\n      outline: none;\n      transition: border-color var(--ease), background var(--ease), opacity var(--ease);\n    }\n\n    .search-field:focus {\n      border-color: rgba(255, 255, 255, 0.18);\n      background: #111;\n    }\n\n    .search-field::placeholder {\n      color: var(--copy-faint);\n    }\n\n    .search-field:disabled {\n      opacity: 0.5;\n      cursor: not-allowed;\n    }\n\n    .suggestions {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 280px;\n      max-height: min(280px, calc(100dvh - 24px));\n      overflow-y: auto;\n      z-index: 2147483001;\n      display: none;\n      padding: 6px;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: rgba(9, 9, 9, 0.98);\n      box-shadow: 0 22px 50px rgba(0, 0, 0, 0.55);\n      backdrop-filter: blur(12px);\n      overscroll-behavior: contain;\n      -webkit-overflow-scrolling: touch;\n    }\n\n    .suggestion {\n      width: 100%;\n      display: flex;\n      justify-content: space-between;\n      gap: 8px;\n      padding: 11px 12px;\n      border: 0;\n      border-radius: 10px;\n      background: transparent;\n      color: var(--white);\n      text-align: left;\n      cursor: pointer;\n    }\n\n    .suggestion small {\n      color: var(--copy-faint);\n      font-size: 11px;\n    }\n\n    .streak-card {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-height: 56px;\n      height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      border: 1px solid var(--line);\n      background: var(--accent);\n      color: #111;\n      text-decoration: none;\n      text-align: center;\n      cursor: default;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .streak-card.is-guest {\n      cursor: pointer;\n    }\n\n    .streak-copy {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      gap: 3px;\n      min-width: 0;\n    }\n\n    .streak-main {\n      display: block;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n    }\n\n    .streak-sub {\n      display: block;\n      color: rgba(17, 17, 17, 0.74);\n      font-size: 10px;\n      line-height: 1.1;\n      font-weight: 700;\n      letter-spacing: 0;\n      white-space: nowrap;\n    }\n\n    .message {\n      display: none;\n      width: 100%;\n      margin: 10px 0 0;\n      padding: 10px 12px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--copy);\n      font-size: 13px;\n      line-height: 1.45;\n      text-align: center;\n    }\n\n    .message.is-visible {\n      display: block;\n    }\n\n    .message.is-success {\n      border-color: rgba(125, 197, 106, 0.36);\n      background: rgba(125, 197, 106, 0.1);\n      color: #daf2d3;\n    }\n\n    .message.is-error {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .message.is-error-final {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error,\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error-final {\n      border-color: rgba(149, 40, 0, 0.32);\n      background: rgba(149, 40, 0, 0.1);\n      color: #ffd7b5;\n    }\n\n    .results-list {\n      display: grid;\n      gap: 12px;\n      margin-top: 12px;\n      width: 100%;\n    }\n\n    .empty-state {\n      padding: 16px;\n      border: 1px dashed rgba(255, 255, 255, 0.15);\n      border-radius: 12px;\n      color: var(--copy-soft);\n      font-size: 13px;\n      line-height: 1.5;\n      text-align: center;\n    }\n\n    .match-card {\n      padding: 18px;\n      border: 1px solid var(--line-faint);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.96), rgba(10, 10, 10, 0.96));\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n      text-align: left;\n    }\n\n    .match-card.is-solved {\n      border-color: rgba(125, 197, 106, 0.34);\n    }\n\n    .match-card.is-answer {\n      border-color: rgba(241, 108, 29, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-card.is-answer {\n      border-color: rgba(149, 40, 0, 0.34);\n    }\n\n    .match-card.is-auto {\n      border-color: rgba(255, 255, 255, 0.16);\n    }\n\n    .match-top {\n      display: grid;\n      grid-template-columns: 92px minmax(0, 1fr) auto;\n      gap: 16px;\n      align-items: center;\n      margin-bottom: 14px;\n    }\n\n    .match-cover {\n      position: relative;\n      width: 92px;\n      height: 92px;\n      border: 1px solid rgba(255, 255, 255, 0.12);\n      border-radius: 50%;\n      background:\n        radial-gradient(circle at 32% 28%, rgba(241, 108, 29, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(241, 108, 29, 0.16), rgba(255, 255, 255, 0.03));\n      overflow: hidden;\n      box-shadow: 0 18px 34px rgba(0, 0, 0, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-cover {\n      background:\n        radial-gradient(circle at 32% 28%, rgba(149, 40, 0, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(149, 40, 0, 0.16), rgba(255, 255, 255, 0.03));\n    }\n\n    .match-cover img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n\n    .match-cover-fallback {\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: 28px;\n      font-weight: 700;\n      letter-spacing: -0.04em;\n      text-transform: uppercase;\n    }\n\n    .match-copy {\n      min-width: 0;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-start;\n      gap: 10px;\n    }\n\n    .match-badge {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-height: 28px;\n      padding: 0 12px;\n      border: 1px solid var(--line);\n      border-radius: 999px;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 700;\n      letter-spacing: -0.01em;\n    }\n\n    .match-name {\n      margin: 0;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(28px, 4vw, 34px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n    }\n\n    .match-sub {\n      margin: -2px 0 0;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-score {\n      min-width: 112px;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-end;\n      gap: 4px;\n      text-align: right;\n    }\n\n    .match-score-main {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(30px, 4.6vw, 38px);\n      line-height: 1;\n      letter-spacing: -0.06em;\n    }\n\n    .match-score-sub {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.14em;\n      text-transform: uppercase;\n    }\n\n    .match-meter {\n      position: relative;\n      height: 16px;\n      border-radius: 999px;\n      background: rgba(255, 255, 255, 0.06);\n      overflow: hidden;\n    }\n\n    .match-meter-fill {\n      position: absolute;\n      inset: 0 auto 0 0;\n      width: 0;\n      border-radius: inherit;\n      transition: width 320ms ease;\n    }\n\n    .match-spotify {\n      margin-top: 12px;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n    }\n\n    .match-spotify iframe {\n      width: 100%;\n      min-height: 152px;\n      border: 0;\n      border-radius: 14px;\n      background: #0b0b0b;\n      display: block;\n    }\n\n    .match-tip {\n      margin: 12px 0 0;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.55;\n    }\n\n    .ranked-summary-panel {\n      margin-top: 4px;\n      padding: 16px;\n      border: 1px solid rgba(149, 40, 0, 0.28);\n      border-radius: 18px;\n      background: rgba(149, 40, 0, 0.08);\n      width: 100%;\n    }\n\n    .ranked-summary-panel.hidden {\n      display: none !important;\n    }\n\n    .ranked-summary-panel.is-record {\n      border-color: rgba(125, 197, 106, 0.32);\n      background: rgba(125, 197, 106, 0.08);\n    }\n\n    .ranked-summary-title {\n      margin: 0 0 10px;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(22px, 4vw, 28px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n      font-weight: 900;\n    }\n\n    .ranked-summary-lines {\n      display: grid;\n      gap: 5px;\n    }\n\n    .ranked-summary-line {\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.5;\n      font-weight: 600;\n    }\n\n    .ranked-summary-line strong {\n      font-weight: 800;\n    }\n\n    @media (max-width: 720px) {\n      .demo-app {\n        width: 100%;\n        max-width: 100%;\n        padding: max(6px, env(safe-area-inset-top, 0px)) 4px calc(10px + env(safe-area-inset-bottom, 0px));\n      }\n\n      .app-shell {\n        display: flex;\n        flex-direction: column;\n        padding: 8px 6px 10px;\n        border: 0;\n        border-radius: 0;\n        background: transparent;\n        box-shadow: none;\n      }\n\n      .app-shell::after {\n        display: none;\n      }\n\n      .ranked-demo-app .controls-shell {\n        order: 1;\n        position: relative;\n        border-top: 0;\n        border-bottom: 1px solid var(--line);\n        padding-top: 10px;\n        padding-bottom: 20px;\n      }\n\n      .ranked-demo-app .controls-shell::after {\n        content: \"Gioca\";\n        position: absolute;\n        left: 50%;\n        bottom: -11px;\n        transform: translateX(-50%) translateY(-2px);\n        padding: 0 14px;\n        background: var(--bg);\n        color: #9E9E9E;\n        font-size: 15px;\n        line-height: 1;\n      }\n\n      .ranked-demo-app .controls-inner {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .topbar {\n        order: 2;\n        padding: 12px 4px 4px;\n      }\n\n      .top-logo {\n        width: min(260px, 66vw);\n      }\n\n      .shell {\n        order: 3;\n        border-top: 0;\n        padding-top: 14px;\n      }\n\n      .input-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n        width: 100%;\n      }\n\n      .attempts-card {\n        order: 1;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n        padding: 0 10px;\n        font-size: 13px;\n      }\n\n      .action-slot {\n        order: 2;\n        width: auto;\n        min-width: 0;\n      }\n\n      .input-shell {\n        order: 3;\n        grid-column: 1 \/ -1;\n        padding: 0;\n        border: 0;\n        border-radius: 0;\n        background: #0b0b0b;\n      }\n\n      .input-grid > .button {\n        order: 4;\n        grid-column: 1 \/ -1;\n      }\n\n      .input-grid > .attempts-card,\n      .input-grid > .button,\n      .action-slot > .secondary-button,\n      .action-slot > .streak-card,\n      .input-grid > .action-slot {\n        width: 100%;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n      }\n\n      .button,\n      .secondary-button,\n      .streak-card,\n      .attempts-card,\n      .action-slot {\n        min-width: 0;\n      }\n\n      .search-field {\n        min-height: 48px;\n        padding: 0 14px;\n        border: 1px solid rgba(255, 255, 255, 0.14);\n        border-radius: 12px;\n        background: #0b0b0b;\n        font-size: 16px;\n      }\n\n      .suggestion {\n        min-height: 48px;\n        padding: 13px 12px;\n      }\n\n      .help-grid {\n        grid-template-columns: 1fr;\n      }\n\n      .help-card span {\n        font-size: 13px;\n        line-height: 1.5;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n      }\n\n      .match-meta-grid > :last-child {\n        grid-column: 1 \/ -1;\n      }\n\n      .match-meta-card {\n        min-height: 62px;\n        padding: 10px 11px;\n      }\n\n      .match-meta-value {\n        font-size: clamp(18px, 5vw, 24px);\n      }\n\n      .match-top {\n        grid-template-columns: 72px minmax(0, 1fr);\n        gap: 12px;\n      }\n\n      .match-score {\n        grid-column: 1 \/ -1;\n        min-width: 0;\n        align-items: flex-start;\n        text-align: left;\n      }\n\n      .match-copy {\n        gap: 8px;\n      }\n\n      .match-card {\n        padding: 12px;\n        border-radius: 16px;\n      }\n\n      .match-cover {\n        width: 72px;\n        height: 72px;\n      }\n\n      .match-name {\n        font-size: clamp(20px, 7vw, 26px);\n      }\n\n      .match-score-main {\n        font-size: 28px;\n      }\n\n      .message {\n        margin-top: 10px;\n        padding: 9px 11px;\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 520px) {\n      .demo-app {\n        padding-inline: 3px;\n      }\n\n      .ranked-demo-app .controls-shell {\n        padding-top: 8px;\n      }\n\n      .top-logo {\n        width: min(232px, 64vw);\n      }\n\n      .shell {\n        padding-top: 8px;\n      }\n\n      .ranked-demo-app .controls-inner {\n        grid-template-columns: 1fr;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .match-sub {\n        font-size: 11px;\n      }\n\n      .match-spotify iframe {\n        min-height: 152px;\n      }\n\n      .match-tip {\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 400px) {\n      .demo-app {\n        padding-inline: 2px;\n      }\n\n      .top-logo {\n        width: min(214px, 62vw);\n      }\n\n      .attempts-card,\n      .button,\n      .secondary-button,\n      .streak-main {\n        font-size: 12px;\n      }\n\n      .streak-sub {\n        font-size: 8px;\n      }\n    }\n\n    @media (hover: none), (pointer: coarse) {\n      .tab:hover,\n      .ghost-button:hover,\n      .button:hover,\n      .secondary-button:hover,\n      .streak-card.is-guest:hover,\n      .suggestion:hover {\n        transform: none;\n      }\n\n      .button:hover,\n      .secondary-button:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .demo-app.is-ranked-mode .match-ranked-form .button:hover,\n      .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary:hover {\n        background: var(--ranked-accent);\n        border-color: var(--ranked-accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover .streak-sub {\n        color: rgba(17, 17, 17, 0.74);\n      }\n\n      .suggestion:hover {\n        background: transparent;\n      }\n\n      .button:active,\n      .secondary-button:active,\n      .streak-card.is-guest:active,\n      .tab:active,\n      .ghost-button:active,\n      .suggestion:active {\n        transform: scale(0.99);\n      }\n    }\n\n    @media (max-width: 720px) {\n      .ranked-demo-app .top-logo {\n        margin-top: 14px !important;\n      }\n\n      .ranked-demo-app .controls-inner {\n        margin-bottom: 18px !important;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"demo-app ranked-demo-app\">\n    <div class=\"app-shell\">\n      <header class=\"topbar\">\n        <img decoding=\"async\" class=\"top-logo\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1.png\" alt=\"BulldogMatch Classificato\">\n      <\/header>\n\n      <section class=\"controls-shell\" aria-label=\"Controlli BulldogMatch\">\n        <div class=\"controls-inner\">\n          <button id=\"rankedModeButton\" class=\"tab is-active\" type=\"button\">Classificata<\/button>\n          <button id=\"helpToggle\" class=\"ghost-button\" type=\"button\">Come si gioca<\/button>\n        <\/div>\n        <div class=\"help-host\">\n          <section id=\"helpPanel\" class=\"help-panel\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Obiettivo<\/strong>\n                <span>Trova l'artista segreto leggendo il punteggio di affinit\u00e0 di ogni tentativo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Affinit\u00e0<\/strong>\n                <span>0-100%. Pi\u00f9 il numero sale, pi\u00f9 il guess condivide tratti col target.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Conta davvero<\/strong>\n                <span>Crew, collab, stile, sottogenere, zona, artisti simili, debutto e tipo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Daily<\/strong>\n                <span>Hai 20 tentativi. La partita del giorno si salva in locale e la streak segue il Match live.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Allenamento<\/strong>\n                <span>Stesso loop del Daily, ma puoi cambiare artista quando vuoi con NUOVO.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Lettura veloce<\/strong>\n                <span>Verde = molto vicino. Giallo = buona base. Arancio = tracce utili. Rosso = lontano.<\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedHelpPanel\" class=\"help-panel hidden\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Timer<\/strong>\n                <span>Hai 5 minuti per fare il punteggio pi\u00f9 alto possibile.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Punti<\/strong>\n                <span>Ogni artista corretto vale 1 punto e aggiunge 30 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Skip<\/strong>\n                <span>Puoi saltare il round corrente, ma perdi 10 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Tentativi<\/strong>\n                <span>Hai 20 tentativi per artista. I guess restano ordinati per affinit\u00e0.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Reveal gratis<\/strong>\n                <span>L'artista corretto del round appena chiuso diventa l'indizio iniziale del round dopo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Record<\/strong>\n                <span>Quando il backend ranked \u00e8 presente, il miglior punteggio viene salvato come nell'originale.<\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n        <\/div>\n      <\/section>\n\n      <main class=\"shell\">\n        <section class=\"game-shell\">\n          <section id=\"standardGamePanel\" class=\"match-standard-panel\">\n            <form id=\"guessForm\" class=\"input-grid\" autocomplete=\"off\">\n              <div id=\"attemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label id=\"inputLabel\" class=\"input-label\" for=\"artistInput\">inserisci un artista<\/label>\n                <input id=\"artistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"gameMsg\">\n                <div id=\"suggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti\"><\/div>\n              <\/div>\n\n              <button id=\"submitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"modeActionButton\" class=\"secondary-button hidden\" type=\"button\">NUOVO<\/button>\n                <a id=\"dailyStreakCard\" class=\"streak-card hidden is-guest\" href=\"https:\/\/bulldogmusik.it\/account-login\/\" aria-label=\"Accedi per salvare la streak\">\n                  <span class=\"streak-copy\">\n                    <span id=\"dailyStreakMain\" class=\"streak-main\">STREAK<\/span>\n                    <span id=\"dailyStreakSub\" class=\"streak-sub\">Accedi per salvarla<\/span>\n                  <\/span>\n                <\/a>\n              <\/div>\n            <\/form>\n\n            <div id=\"gameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"resultsList\" class=\"results-list\">\n              <div class=\"empty-state\">Nessun tentativo ancora.<\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedGamePanel\" class=\"match-ranked-panel hidden\">\n            <section class=\"match-meta-grid\" aria-label=\"Statistiche Classificata\">\n              <article class=\"match-meta-card is-accent\">\n                <span class=\"match-meta-label\">Tempo<\/span>\n                <strong id=\"rankedTimerValue\" class=\"match-meta-value\">5:00<\/strong>\n              <\/article>\n              <article class=\"match-meta-card\">\n                <span class=\"match-meta-label\">Punti<\/span>\n                <strong id=\"rankedScoreValue\" class=\"match-meta-value\">0<\/strong>\n              <\/article>\n              <button id=\"rankedHelpToggle\" class=\"ranked-meta-button\" type=\"button\">Come si gioca<\/button>\n            <\/section>\n\n            <section id=\"rankedHelpPanelInline\" class=\"help-panel\" aria-hidden=\"true\">\n              <div class=\"help-grid\">\n                <div class=\"help-card\">\n                  <strong>Timer<\/strong>\n                  <span>Hai 5 minuti per fare il punteggio pi\u00f9 alto possibile.<\/span>\n                <\/div>\n                <div class=\"help-card\">\n                  <strong>Punti<\/strong>\n                  <span>Ogni artista corretto vale 1 punto e aggiunge 30 secondi.<\/span>\n                <\/div>\n                <div class=\"help-card\">\n                  <strong>Skip<\/strong>\n                  <span>Puoi saltare il round corrente con il pulsante SKIP, ma perdi 10 secondi.<\/span>\n                <\/div>\n                <div class=\"help-card\">\n                  <strong>Tentativi<\/strong>\n                  <span>Hai 20 tentativi per artista. Se finisci i tentativi si passa al prossimo senza penalit\u00e0.<\/span>\n                <\/div>\n                <div class=\"help-card\">\n                  <strong>Reveal gratis<\/strong>\n                  <span>L'artista corretto del round appena chiuso diventa l'indizio iniziale del round dopo.<\/span>\n                <\/div>\n                <div class=\"help-card\">\n                  <strong>Record<\/strong>\n                  <span>Alla fine del gioco il tuo record viene salvato e inserito in classifica.<\/span>\n                <\/div>\n              <\/div>\n            <\/section>\n\n            <form id=\"rankedGuessForm\" class=\"input-grid match-ranked-form\" autocomplete=\"off\">\n              <div id=\"rankedAttemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label class=\"input-label\" for=\"rankedArtistInput\">inserisci un artista<\/label>\n                <input id=\"rankedArtistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"rankedGameMsg\">\n                <div id=\"rankedSuggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti classificata\"><\/div>\n              <\/div>\n\n              <button id=\"rankedSubmitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"rankedSideActionButton\" class=\"secondary-button is-primary\" type=\"button\">INIZIA<\/button>\n              <\/div>\n            <\/form>\n\n            <section id=\"rankedSummaryPanel\" class=\"ranked-summary-panel hidden\" aria-live=\"polite\">\n              <h3 id=\"rankedSummaryTitle\" class=\"ranked-summary-title\">Partita conclusa<\/h3>\n              <div id=\"rankedSummaryLines\" class=\"ranked-summary-lines\"><\/div>\n            <\/section>\n\n            <div id=\"rankedGameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"rankedResultsList\" class=\"results-list\">\n              <div class=\"empty-state\">ACCEDI per giocare.<\/div>\n            <\/div>\n          <\/section>\n        <\/section>\n      <\/main>\n    <\/div>\n  <\/div>\n\n  <script>\n    (async function () {\n      const bootstrapScript = document.currentScript;\n\n      async function loadScriptSource(src) {\n        return new Promise((resolve) => {\n          const existing = Array.from(document.querySelectorAll(\"script[src]\")).find((node) => node.src === src);\n          if (existing && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            resolve(true);\n            return;\n          }\n\n          const script = document.createElement(\"script\");\n          script.src = src;\n          script.async = false;\n          script.onload = () => resolve(true);\n          script.onerror = () => resolve(false);\n          document.head.appendChild(script);\n        });\n      }\n\n      async function ensureArtistsLoaded() {\n        if (Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n          return window.bulldogleArtists;\n        }\n\n        const sources = [\n          \".\/artists.js\",\n          \"artists.js\",\n          \"\/bulldogle\/artists.js?v=31\"\n        ];\n\n        if (window.location && \/^https?:\/i.test(window.location.protocol) && window.location.origin) {\n          sources.push(window.location.origin + \"\/bulldogle\/artists.js?v=31\");\n        }\n\n        sources.push(\"https:\/\/bulldogmusik.it\/bulldogle\/artists.js?v=31\");\n\n        for (const source of sources) {\n          const loaded = await loadScriptSource(source);\n          if (loaded && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            return window.bulldogleArtists;\n          }\n        }\n\n        return [];\n      }\n\n      const artistsSource = await ensureArtistsLoaded();\n      const matchConfig = window.bulldogleMatchConfig || {};\n      const rankedConfig = window.bulldogleRankedConfig || {};\n      const guestLoginUrl = \"https:\/\/bulldogmusik.it\/account-login\/\";\n      const CLASSIC_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png\";\n      const RANKED_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1.png\";\n      const STANDARD_ATTEMPTS = 20;\n      const RANKED_ATTEMPTS = 20;\n      const RANKED_INITIAL_TIME = 300;\n      const RANKED_CORRECT_BONUS = 30;\n      const RANKED_SKIP_PENALTY = 10;\n      const RANKED_ROUND_TRANSITION_MS = 1000;\n      const RANKED_GAME_KEY = \"match\";\n      const hasRankedBackend = Boolean(rankedConfig && rankedConfig.ajaxUrl && rankedConfig.nonce);\n      const spotifyThumbStorageKey = \"bulldogmatch-spotify-thumbs-v1\";\n\n      const regionCoords = {\n        \"Abruzzo\": { lat: 42.3, lon: 13.9 },\n        \"Basilicata\": { lat: 40.6, lon: 15.8 },\n        \"Calabria\": { lat: 38.9, lon: 16.6 },\n        \"Campania\": { lat: 40.8, lon: 14.3 },\n        \"Emilia-Romagna\": { lat: 44.5, lon: 11.3 },\n        \"Friuli-Venezia Giulia\": { lat: 46.1, lon: 13.1 },\n        \"Lazio\": { lat: 41.9, lon: 12.5 },\n        \"Liguria\": { lat: 44.4, lon: 8.9 },\n        \"Lombardia\": { lat: 45.5, lon: 9.2 },\n        \"Marche\": { lat: 43.6, lon: 13.5 },\n        \"Molise\": { lat: 41.6, lon: 14.6 },\n        \"Piemonte\": { lat: 45.1, lon: 7.7 },\n        \"Puglia\": { lat: 41.1, lon: 16.9 },\n        \"Sardegna\": { lat: 40.1, lon: 9.0 },\n        \"Sicilia\": { lat: 37.6, lon: 14.0 },\n        \"Toscana\": { lat: 43.8, lon: 11.2 },\n        \"Trentino-Alto Adige\": { lat: 46.5, lon: 11.3 },\n        \"Umbria\": { lat: 43.1, lon: 12.4 },\n        \"Valle d'Aosta\": { lat: 45.7, lon: 7.3 },\n        \"Veneto\": { lat: 45.4, lon: 12.3 },\n        \"Estero\": null\n      };\n\n      const spotifyThumbCache = new Map();\n      const spotifyThumbRequests = new Map();\n      let standardMessageTimeoutId = 0;\n      let rankedMessageTimeoutId = 0;\n      let dailyStreakLoading = false;\n      let dailyStreakLoaded = false;\n\n      try {\n        const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n        Object.keys(storedThumbs).forEach((spotifyId) => {\n          if (storedThumbs[spotifyId]) {\n            spotifyThumbCache.set(spotifyId, String(storedThumbs[spotifyId]));\n          }\n        });\n      } catch (_) {}\n\n      const artists = artistsSource.map((artist) => ({\n        id: Number(artist.id),\n        name: String(artist.name || \"\").trim(),\n        city: String(artist.city || \"\").trim(),\n        region: String(artist.region || \"\").trim(),\n        debutYear: Number(artist.debutYear || 0),\n        members: String(artist.members || \"\").trim(),\n        gender: String(artist.gender || \"\").trim(),\n        popularity: String(artist.popularity || \"\").trim().toLowerCase(),\n        spotifyId: String(artist.spotifyId || \"\").trim(),\n        crew: String(artist.crew || \"\").trim(),\n        style: String(artist.style || \"\").trim(),\n        collab: String(artist.collab || \"\").trim(),\n        subgenre: String(artist.subgenre || \"\").trim(),\n        collabmid: String(artist.collabmid || \"\").trim(),\n        similar: String(artist.similar || \"\").trim(),\n        birthYear: Number(artist.birthYear || 0),\n        tip: String(artist.tip || \"\").trim()\n      })).filter((artist) => artist.id && artist.name);\n\n      const demoRoots = Array.from(document.querySelectorAll(\".demo-app\"));\n      const appRoot = bootstrapScript &&\n        bootstrapScript.previousElementSibling &&\n        bootstrapScript.previousElementSibling.classList &&\n        bootstrapScript.previousElementSibling.classList.contains(\"demo-app\")\n          ? bootstrapScript.previousElementSibling\n          : demoRoots[demoRoots.length - 1];\n\n      if (!appRoot) return;\n\n      const elements = {\n        topLogo: appRoot.querySelector(\".top-logo\"),\n        tabs: Array.from(appRoot.querySelectorAll(\".tab[data-mode]\")),\n        rankedModeButton: appRoot.querySelector(\"#rankedModeButton\"),\n        helpToggle: appRoot.querySelector(\"#rankedHelpToggle\") || appRoot.querySelector(\"#helpToggle\"),\n        helpPanel: appRoot.querySelector(\"#helpPanel\"),\n        rankedHelpPanel: appRoot.querySelector(\"#rankedHelpPanelInline\") || appRoot.querySelector(\"#rankedHelpPanel\"),\n        standardGamePanel: appRoot.querySelector(\"#standardGamePanel\"),\n        rankedGamePanel: appRoot.querySelector(\"#rankedGamePanel\"),\n        attemptsCard: appRoot.querySelector(\"#attemptsCard\"),\n        guessForm: appRoot.querySelector(\"#guessForm\"),\n        inputLabel: appRoot.querySelector(\"#inputLabel\"),\n        input: appRoot.querySelector(\"#artistInput\"),\n        suggestions: appRoot.querySelector(\"#suggestions\"),\n        submitButton: appRoot.querySelector(\"#submitButton\"),\n        modeActionButton: appRoot.querySelector(\"#modeActionButton\"),\n        dailyStreakCard: appRoot.querySelector(\"#dailyStreakCard\"),\n        dailyStreakMain: appRoot.querySelector(\"#dailyStreakMain\"),\n        dailyStreakSub: appRoot.querySelector(\"#dailyStreakSub\"),\n        message: appRoot.querySelector(\"#gameMsg\"),\n        resultsList: appRoot.querySelector(\"#resultsList\"),\n        rankedTimerValue: appRoot.querySelector(\"#rankedTimerValue\"),\n        rankedScoreValue: appRoot.querySelector(\"#rankedScoreValue\"),\n        rankedRoundValue: appRoot.querySelector(\"#rankedRoundValue\"),\n        rankedAttemptsCard: appRoot.querySelector(\"#rankedAttemptsCard\"),\n        rankedGuessForm: appRoot.querySelector(\"#rankedGuessForm\"),\n        rankedInput: appRoot.querySelector(\"#rankedArtistInput\"),\n        rankedSuggestions: appRoot.querySelector(\"#rankedSuggestions\"),\n        rankedSubmitButton: appRoot.querySelector(\"#rankedSubmitButton\"),\n        rankedSideActionButton: appRoot.querySelector(\"#rankedSideActionButton\"),\n        rankedSummaryPanel: appRoot.querySelector(\"#rankedSummaryPanel\"),\n        rankedSummaryTitle: appRoot.querySelector(\"#rankedSummaryTitle\"),\n        rankedSummaryLines: appRoot.querySelector(\"#rankedSummaryLines\"),\n        rankedMessage: appRoot.querySelector(\"#rankedGameMsg\"),\n        rankedResultsList: appRoot.querySelector(\"#rankedResultsList\")\n      };\n\n      if (!elements.guessForm || !elements.input || !elements.submitButton || !elements.suggestions) {\n        return;\n      }\n\n      if (elements.suggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.suggestions);\n      }\n\n      if (elements.rankedSuggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.rankedSuggestions);\n      }\n\n      const italyDateInfo = getItalyDateInfo();\n      const dailyStorageKey = \"bulldogle_affinity_\" + italyDateInfo.dayKey;\n      const rankedLocalBestKey = \"bulldogmatch_ranked_demo_best\";\n\n      const app = {\n        mode: \"ranked\",\n        modes: {\n          classic: createStandardState(getDailyTarget()),\n          practice: createPracticeState()\n        },\n        ranked: createRankedState()\n      };\n\n      if (!artists.length) {\n        elements.message.textContent = \"artists.js non e stato caricato correttamente.\";\n        elements.message.classList.add(\"is-visible\", \"is-error\");\n        disableStandardInput(true);\n        return;\n      }\n\n      function createStandardState(targetArtist) {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: targetArtist\n        };\n      }\n\n      function createPracticeState() {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: 1,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n      }\n\n      function createRankedState() {\n        return {\n          active: false,\n          preparing: false,\n          submitting: false,\n          complete: false,\n          betweenRounds: false,\n          sessionToken: \"\",\n          currentArtist: null,\n          score: 0,\n          timeLeft: RANKED_INITIAL_TIME,\n          roundAttempts: 0,\n          roundGuessedIds: new Set(),\n          usedTargetIds: new Set(),\n          freeHintArtist: null,\n          results: [],\n          timerId: 0,\n          roundTransitionId: 0,\n          summaryTitle: \"Partita conclusa\",\n          summaryLines: [],\n          isRecord: false,\n          localBest: readLocalRankedBest()\n        };\n      }\n\n      function readLocalRankedBest() {\n        try {\n          return Number(window.localStorage.getItem(rankedLocalBestKey) || 0);\n        } catch (_) {\n          return 0;\n        }\n      }\n\n      function writeLocalRankedBest(score) {\n        const best = Math.max(readLocalRankedBest(), Number(score || 0));\n        try {\n          window.localStorage.setItem(rankedLocalBestKey, String(best));\n        } catch (_) {}\n        return best;\n      }\n\n      function normalize(value) {\n        return String(value || \"\").trim().toLowerCase();\n      }\n\n      function getItalyDateInfo() {\n        const formatter = new Intl.DateTimeFormat(\"en-CA\", {\n          timeZone: \"Europe\/Rome\",\n          year: \"numeric\",\n          month: \"2-digit\",\n          day: \"2-digit\"\n        });\n\n        const parts = formatter.formatToParts(new Date());\n        const year = Number(parts.find((part) => part.type === \"year\").value);\n        const month = Number(parts.find((part) => part.type === \"month\").value);\n        const day = Number(parts.find((part) => part.type === \"day\").value);\n\n        return {\n          dayKey: year + \"-\" + String(month).padStart(2, \"0\") + \"-\" + String(day).padStart(2, \"0\"),\n          dayIndex: Math.floor(Date.UTC(year, month - 1, day) \/ 86400000)\n        };\n      }\n\n      function getDailyTarget() {\n        return artists[(italyDateInfo.dayIndex + 137) % artists.length] || artists[0];\n      }\n\n      function pickRandomArtist(excludedIds) {\n        const blocked = excludedIds || new Set();\n        let pool = artists.filter((artist) => !blocked.has(artist.id));\n        if (!pool.length) pool = artists.slice();\n        return pool[Math.floor(Math.random() * pool.length)];\n      }\n\n      function getCurrentStandardState() {\n        return app.modes[app.mode];\n      }\n\n      function findArtistByName(name) {\n        const normalized = normalize(name);\n        return artists.find((artist) => normalize(artist.name) === normalized);\n      }\n\n      function normalizeCommaSeparatedList(listString) {\n        if (!listString) return [];\n        return listString\n          .split(\",\")\n          .map((item) => item.trim().toLowerCase())\n          .filter(Boolean);\n      }\n\n      function isInList(artistName, listString) {\n        if (!listString || !artistName) return false;\n        return normalizeCommaSeparatedList(listString).includes(artistName.toLowerCase());\n      }\n\n      function sharesListValue(firstListString, secondListString) {\n        const firstList = normalizeCommaSeparatedList(firstListString);\n        const secondList = normalizeCommaSeparatedList(secondListString);\n        if (!firstList.length || !secondList.length) return false;\n\n        const secondSet = new Set(secondList);\n        return firstList.some((item) => secondSet.has(item));\n      }\n\n      function calculateAffinity(guess, target) {\n        if (guess.id === target.id) return 100;\n\n        let score = 0;\n        const W_CREW = 24;\n        const W_COLLAB = 21;\n        const W_STYLE = 16;\n        const W_SUBGENRE = 10;\n        const W_REGION = 3;\n        const W_COLLABMID = 15;\n        const W_SIMILAR = 19;\n        const W_DEBUT_YEAR = 3;\n        const W_MEMBERS = 3;\n        const W_BIRTH_YEAR = 0;\n\n        if (sharesListValue(guess.crew, target.crew)) score += W_CREW;\n        if (isInList(target.name, guess.collab) || isInList(guess.name, target.collab)) score += W_COLLAB;\n        if (guess.style && target.style && guess.style === target.style) score += W_STYLE;\n        if (guess.subgenre && target.subgenre && guess.subgenre === target.subgenre) score += W_SUBGENRE;\n\n        if (guess.city === target.city) {\n          score += W_REGION;\n        } else if (guess.region === target.region) {\n          score += W_REGION * 0.6;\n        } else {\n          const g = regionCoords[guess.region];\n          const t = regionCoords[target.region];\n          if (g && t) {\n            const latDiff = g.lat - t.lat;\n            const lonDiff = g.lon - t.lon;\n            const dist = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);\n            const maxDist = 12;\n            if (dist < maxDist) {\n              score += (W_REGION * 0.3) * (1 - dist \/ maxDist);\n            }\n          }\n        }\n\n        if (isInList(target.name, guess.collabmid) || isInList(guess.name, target.collabmid)) score += W_COLLABMID;\n\n        const yearDiff = Math.abs(Number(guess.debutYear || 0) - Number(target.debutYear || 0));\n        if (yearDiff === 0) score += W_DEBUT_YEAR;\n        else if (yearDiff <= 2) score += W_DEBUT_YEAR * 0.8;\n        else if (yearDiff <= 5) score += W_DEBUT_YEAR * 0.5;\n        else if (yearDiff <= 10) score += W_DEBUT_YEAR * 0.2;\n\n        if (guess.members === target.members) score += W_MEMBERS;\n        if (isInList(target.name, guess.similar) || isInList(guess.name, target.similar)) score += W_SIMILAR;\n\n        if (guess.birthYear && target.birthYear) {\n          const birthDiff = Math.abs(Number(guess.birthYear || 0) - Number(target.birthYear || 0));\n          if (birthDiff === 0) score += W_BIRTH_YEAR;\n          else if (birthDiff <= 2) score += W_BIRTH_YEAR * 0.8;\n          else if (birthDiff <= 5) score += W_BIRTH_YEAR * 0.5;\n          else if (birthDiff <= 10) score += W_BIRTH_YEAR * 0.2;\n        }\n\n        return Math.min(99, Math.floor(score));\n      }\n\n      function getAffinityTone(affinity) {\n        if (affinity >= 100) return \"perfect\";\n        if (affinity >= 80) return \"high\";\n        if (affinity >= 50) return \"medium\";\n        if (affinity >= 30) return \"warm\";\n        return \"low\";\n      }\n\n      function getAffinityColor(affinity, ranked) {\n        if (affinity >= 100) return \"#7dc56a\";\n        if (affinity >= 80) return \"#7dc56a\";\n        if (affinity >= 50) return \"#f0b955\";\n        if (affinity >= 30) return ranked ? \"#952800\" : \"#f16c1d\";\n        return \"#ff7e59\";\n      }\n\n      function getAffinityDescriptor(affinity) {\n        if (affinity >= 100) return \"Hai indovinato\";\n        if (affinity >= 80) return \"Molto vicino\";\n        if (affinity >= 50) return \"Buona affinit\u00e0\";\n        if (affinity >= 30) return \"Tracce interessanti\";\n        return \"Molto lontano\";\n      }\n\n      function getArtistInitials(name) {\n        const parts = String(name || \"\").trim().split(\/\\s+\/).filter(Boolean);\n        if (!parts.length) return \"BM\";\n        return parts.slice(0, 2).map((part) => part.charAt(0)).join(\"\");\n      }\n\n      function persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl) {\n        if (!spotifyId || !thumbnailUrl) return;\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          storedThumbs[spotifyId] = thumbnailUrl;\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      function clearSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return;\n        spotifyThumbCache.delete(spotifyId);\n        spotifyThumbRequests.delete(spotifyId);\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          delete storedThumbs[spotifyId];\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      async function getSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return \"\";\n        if (spotifyThumbCache.has(spotifyId)) return spotifyThumbCache.get(spotifyId);\n        if (spotifyThumbRequests.has(spotifyId)) return spotifyThumbRequests.get(spotifyId);\n\n        const request = (async function () {\n          const sources = [\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"https:\/\/open.spotify.com\/artist\/\" + spotifyId),\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"spotify:artist:\" + spotifyId)\n          ];\n\n          for (const source of sources) {\n            try {\n              const response = await fetch(source, { credentials: \"omit\" });\n              if (!response.ok) continue;\n              const payload = await response.json();\n              const thumbnailUrl = payload && payload.thumbnail_url ? String(payload.thumbnail_url) : \"\";\n              if (thumbnailUrl) {\n                spotifyThumbCache.set(spotifyId, thumbnailUrl);\n                persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl);\n                return thumbnailUrl;\n              }\n            } catch (_) {}\n          }\n\n          return \"\";\n        })().finally(function () {\n          spotifyThumbRequests.delete(spotifyId);\n        });\n\n        spotifyThumbRequests.set(spotifyId, request);\n        return request;\n      }\n\n      function buildArtistCover(artist) {\n        const cover = document.createElement(\"div\");\n        cover.className = \"match-cover\";\n\n        const fallback = document.createElement(\"span\");\n        fallback.className = \"match-cover-fallback\";\n        fallback.textContent = getArtistInitials(artist.name);\n        cover.appendChild(fallback);\n\n        if (artist.spotifyId) {\n          getSpotifyThumbnailUrl(artist.spotifyId).then((thumbnailUrl) => {\n            if (!thumbnailUrl) return;\n            const image = document.createElement(\"img\");\n            image.alt = artist.name;\n            image.loading = \"eager\";\n            image.decoding = \"async\";\n            image.referrerPolicy = \"no-referrer\";\n\n            function applyThumbnail() {\n              cover.replaceChildren(image);\n            }\n\n            function handleThumbnailError() {\n              clearSpotifyThumbnailUrl(artist.spotifyId);\n            }\n\n            image.addEventListener(\"error\", handleThumbnailError, { once: true });\n            image.addEventListener(\"load\", applyThumbnail, { once: true });\n            image.src = thumbnailUrl;\n\n            if (image.complete) {\n              if (image.naturalWidth > 0) {\n                applyThumbnail();\n              } else {\n                handleThumbnailError();\n              }\n            }\n          });\n        }\n\n        return cover;\n      }\n\n      function buildSpotifyEmbed(spotifyId) {\n        if (!spotifyId) return null;\n        const wrap = document.createElement(\"div\");\n        wrap.className = \"match-spotify\";\n        const iframe = document.createElement(\"iframe\");\n        iframe.src = \"https:\/\/open.spotify.com\/embed\/artist\/\" + spotifyId + \"?utm_source=generator&theme=0\";\n        iframe.allow = \"autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture\";\n        iframe.loading = \"lazy\";\n        wrap.appendChild(iframe);\n        return wrap;\n      }\n\n      function buildArtistTip(tip) {\n        const cleanTip = String(tip || \"\").trim();\n        if (!cleanTip) return null;\n        const copy = document.createElement(\"p\");\n        copy.className = \"match-tip\";\n        copy.textContent = cleanTip;\n        return copy;\n      }\n\n      function buildResultEntry(artist, targetArtist, meta) {\n        const affinity = calculateAffinity(artist, targetArtist);\n        return {\n          artist: artist,\n          affinity: affinity,\n          kind: meta.kind || \"guess\",\n          label: meta.label || \"Tentativo\",\n          subcopy: meta.subcopy || getAffinityDescriptor(affinity),\n          solved: artist.id === targetArtist.id,\n          spotifyId: meta.spotifyId || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.spotifyId : \"\"),\n          tip: meta.tip || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.tip : \"\"),\n          ranked: Boolean(meta.ranked)\n        };\n      }\n\n      function buildMatchResultCard(result) {\n        const article = document.createElement(\"article\");\n        article.className = \"match-card\";\n        if (result.solved) article.classList.add(\"is-solved\");\n        if (result.kind === \"answer\") article.classList.add(\"is-answer\");\n        if (result.kind === \"auto\") article.classList.add(\"is-auto\");\n\n        const top = document.createElement(\"div\");\n        top.className = \"match-top\";\n\n        const cover = buildArtistCover(result.artist);\n        const copy = document.createElement(\"div\");\n        copy.className = \"match-copy\";\n\n        const badge = document.createElement(\"span\");\n        badge.className = \"match-badge\";\n        badge.textContent = result.label;\n\n        const name = document.createElement(\"h4\");\n        name.className = \"match-name\";\n        name.textContent = result.artist.name;\n\n        const sub = document.createElement(\"p\");\n        sub.className = \"match-sub\";\n        sub.textContent = result.subcopy || getAffinityDescriptor(result.affinity);\n\n        copy.appendChild(badge);\n        copy.appendChild(name);\n        copy.appendChild(sub);\n\n        const score = document.createElement(\"div\");\n        score.className = \"match-score\";\n\n        const scoreMain = document.createElement(\"span\");\n        scoreMain.className = \"match-score-main\";\n        scoreMain.textContent = result.affinity + \"%\";\n\n        const scoreSub = document.createElement(\"span\");\n        scoreSub.className = \"match-score-sub\";\n        scoreSub.textContent = \"Affinit\u00e0\";\n\n        score.appendChild(scoreMain);\n        score.appendChild(scoreSub);\n\n        top.appendChild(cover);\n        top.appendChild(copy);\n        top.appendChild(score);\n\n        const meter = document.createElement(\"div\");\n        meter.className = \"match-meter\";\n        const fill = document.createElement(\"div\");\n        fill.className = \"match-meter-fill\";\n        fill.style.width = result.affinity + \"%\";\n        fill.style.background = getAffinityColor(result.affinity, result.ranked);\n        meter.appendChild(fill);\n\n        article.appendChild(top);\n        article.appendChild(meter);\n\n        const spotifyEmbed = buildSpotifyEmbed(result.spotifyId);\n        if (spotifyEmbed) article.appendChild(spotifyEmbed);\n        if (result.tip) {\n          const tipNode = buildArtistTip(result.tip);\n          if (tipNode) article.appendChild(tipNode);\n        }\n\n        return article;\n      }\n\n      function disableStandardInput(disabled) {\n        elements.input.disabled = disabled;\n        elements.submitButton.disabled = disabled;\n      }\n\n      function disableRankedInput(disabled) {\n        elements.rankedInput.disabled = disabled;\n        elements.rankedSubmitButton.disabled =\n          disabled ||\n          !app.ranked.active ||\n          app.ranked.betweenRounds ||\n          app.ranked.preparing ||\n          app.ranked.submitting;\n      }\n\n      function getViewportMetrics() {\n        if (window.visualViewport) {\n          return {\n            width: window.visualViewport.width,\n            height: window.visualViewport.height,\n            offsetLeft: window.visualViewport.offsetLeft,\n            offsetTop: window.visualViewport.offsetTop\n          };\n        }\n\n        return {\n          width: window.innerWidth,\n          height: window.innerHeight,\n          offsetLeft: 0,\n          offsetTop: 0\n        };\n      }\n\n      function positionSuggestionsFor(inputElement, suggestionElement) {\n        const rect = inputElement.getBoundingClientRect();\n        const viewport = getViewportMetrics();\n        const viewportPadding = 10;\n        const width = Math.min(rect.width, viewport.width - viewportPadding * 2);\n        const left = Math.max(\n          viewport.offsetLeft + viewportPadding,\n          Math.min(rect.left, viewport.offsetLeft + viewport.width - width - viewportPadding)\n        );\n        const spaceBelow = viewport.offsetTop + viewport.height - rect.bottom - viewportPadding;\n        const maxHeight = Math.max(140, Math.min(280, spaceBelow - 8));\n\n        suggestionElement.style.width = width + \"px\";\n        suggestionElement.style.left = left + \"px\";\n        suggestionElement.style.top = Math.max(viewportPadding, rect.bottom + 8) + \"px\";\n        suggestionElement.style.maxHeight = maxHeight + \"px\";\n      }\n\n      function hideSuggestions() {\n        elements.suggestions.style.display = \"none\";\n      }\n\n      function showSuggestions() {\n        positionSuggestionsFor(elements.input, elements.suggestions);\n        elements.suggestions.style.display = \"block\";\n      }\n\n      function hideRankedSuggestions() {\n        elements.rankedSuggestions.style.display = \"none\";\n      }\n\n      function showRankedSuggestions() {\n        positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        elements.rankedSuggestions.style.display = \"block\";\n      }\n\n      function getCurrentHelpPanel() {\n        return app.mode === \"ranked\" ? elements.rankedHelpPanel : elements.helpPanel;\n      }\n\n      function closeAllHelpPanels() {\n        [elements.helpPanel, elements.rankedHelpPanel].forEach((panel) => {\n          if (!panel) return;\n          panel.classList.remove(\"is-open\");\n          panel.setAttribute(\"aria-hidden\", \"true\");\n        });\n        elements.helpToggle.classList.remove(\"is-active\");\n      }\n\n      function syncHelpToggleState() {\n        const panel = getCurrentHelpPanel();\n        elements.helpToggle.classList.toggle(\"is-active\", !!panel && panel.classList.contains(\"is-open\"));\n      }\n\n      function syncHelpPanelVisibility() {\n        const rankedActive = app.mode === \"ranked\";\n        if (elements.helpPanel) {\n          elements.helpPanel.classList.toggle(\"hidden\", rankedActive);\n        }\n        if (elements.rankedHelpPanel) {\n          elements.rankedHelpPanel.classList.toggle(\"hidden\", !rankedActive);\n        }\n      }\n\n      function setMessage(text, tone) {\n        if (standardMessageTimeoutId) {\n          window.clearTimeout(standardMessageTimeoutId);\n          standardMessageTimeoutId = 0;\n        }\n\n        elements.message.className = \"message\";\n        if (text && tone) {\n          elements.message.textContent = text;\n          elements.message.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\" && tone !== \"error-final\") {\n            standardMessageTimeoutId = window.setTimeout(function () {\n              elements.message.className = \"message\";\n              elements.message.textContent = \"\";\n              standardMessageTimeoutId = 0;\n            }, 3000);\n          }\n          return;\n        }\n\n        elements.message.textContent = \"\";\n      }\n\n      function setRankedMessage(text, tone) {\n        if (rankedMessageTimeoutId) {\n          window.clearTimeout(rankedMessageTimeoutId);\n          rankedMessageTimeoutId = 0;\n        }\n\n        elements.rankedMessage.className = \"message\";\n        if (text && tone) {\n          elements.rankedMessage.textContent = text;\n          elements.rankedMessage.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\") {\n            rankedMessageTimeoutId = window.setTimeout(function () {\n              elements.rankedMessage.className = \"message\";\n              elements.rankedMessage.textContent = \"\";\n              rankedMessageTimeoutId = 0;\n            }, 2200);\n          }\n          return;\n        }\n\n        elements.rankedMessage.textContent = \"\";\n      }\n\n      function renderBranding() {\n        const rankedActive = app.mode === \"ranked\";\n        const nextSrc = rankedActive ? RANKED_LOGO_URL : CLASSIC_LOGO_URL;\n        const nextAlt = rankedActive ? \"BulldogMatch Classificato\" : \"BulldogMatch\";\n\n        if (elements.topLogo.getAttribute(\"src\") !== nextSrc) {\n          elements.topLogo.setAttribute(\"src\", nextSrc);\n        }\n\n        elements.topLogo.setAttribute(\"alt\", nextAlt);\n        appRoot.classList.toggle(\"is-ranked-mode\", rankedActive);\n        document.body.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n        document.documentElement.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n      }\n\n      function renderTabs() {\n        elements.tabs.forEach((tab) => {\n          tab.classList.toggle(\"is-active\", tab.dataset.mode === app.mode);\n        });\n        elements.rankedModeButton.classList.toggle(\"is-active\", app.mode === \"ranked\");\n      }\n\n      function renderPanels() {\n        const rankedActive = app.mode === \"ranked\";\n        elements.standardGamePanel.classList.toggle(\"hidden\", rankedActive);\n        elements.rankedGamePanel.classList.toggle(\"hidden\", !rankedActive);\n        syncHelpPanelVisibility();\n      }\n\n      function renderStandardAttempts() {\n        const state = getCurrentStandardState();\n        const attemptsLeft = Math.max(0, state.maxAttempts - state.attempts);\n        elements.attemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderStandardActionSlot() {\n        if (app.mode === \"classic\") {\n          elements.modeActionButton.classList.add(\"hidden\");\n          elements.dailyStreakCard.classList.remove(\"hidden\");\n          loadDailyStreakStatus();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.add(\"hidden\");\n        elements.modeActionButton.classList.remove(\"hidden\");\n        elements.modeActionButton.textContent = \"NUOVO\";\n        elements.modeActionButton.disabled = false;\n      }\n\n      function renderStandardInputState() {\n        const state = getCurrentStandardState();\n        disableStandardInput(state.complete);\n        elements.inputLabel.textContent = \"inserisci un artista\";\n        if (app.mode === \"classic\" && state.complete) {\n          elements.input.placeholder = \"Torna domani!\";\n        } else if (app.mode === \"practice\" && state.complete) {\n          elements.input.placeholder = \"Premi NUOVO per ricominciare\";\n        } else {\n          elements.input.placeholder = \"inserisci un artista\";\n        }\n      }\n\n      function renderStandardResults() {\n        const state = getCurrentStandardState();\n        elements.resultsList.innerHTML = \"\";\n\n        if (!state.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = \"Nessun tentativo ancora.\";\n          elements.resultsList.appendChild(empty);\n          return;\n        }\n\n        state.results.forEach((result) => {\n          elements.resultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedMeta() {\n        elements.rankedTimerValue.textContent = formatTime(app.ranked.timeLeft);\n        elements.rankedScoreValue.textContent = String(app.ranked.score);\n        if (elements.rankedRoundValue) {\n          elements.rankedRoundValue.textContent = app.ranked.active\n            ? \"Artista #\" + String(app.ranked.score + 1)\n            : (app.ranked.complete ? \"Fine\" : \"Pronto\");\n        }\n      }\n\n      function renderRankedAttempts() {\n        const attemptsLeft = Math.max(0, RANKED_ATTEMPTS - app.ranked.roundAttempts);\n        elements.rankedAttemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderRankedInputState() {\n        disableRankedInput(!app.ranked.active);\n        elements.rankedInput.placeholder = app.ranked.active ? \"inserisci un artista\" : (app.ranked.complete ? \"Partita conclusa\" : \"inserisci un artista\");\n      }\n\n      function renderRankedActionButton() {\n        const button = elements.rankedSideActionButton;\n        button.className = \"secondary-button is-primary\";\n        button.disabled = false;\n\n        if (app.ranked.preparing || app.ranked.submitting) {\n          button.textContent = \"...\";\n          button.disabled = true;\n          button.dataset.action = \"wait\";\n          return;\n        }\n\n        if (app.ranked.active) {\n          if (app.ranked.betweenRounds) {\n            button.textContent = \"ATTENDI\";\n            button.disabled = true;\n            button.dataset.action = \"wait\";\n            return;\n          }\n          button.textContent = \"SKIP\";\n          button.dataset.action = \"skip\";\n          return;\n        }\n\n        if (app.ranked.complete) {\n          button.textContent = \"RIGIOCA\";\n          button.dataset.action = \"restart\";\n          return;\n        }\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          button.textContent = \"ACCEDI\";\n          button.dataset.action = \"login\";\n          return;\n        }\n\n        button.textContent = \"INIZIA\";\n        button.dataset.action = \"start\";\n      }\n\n      function renderRankedResults() {\n        elements.rankedResultsList.innerHTML = \"\";\n\n        if (!app.ranked.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = app.ranked.complete\n            ? \"La partita e finita. Puoi controllare il riepilogo finale.\"\n            : (app.ranked.active\n              ? \"I tuoi tentativi appariranno qui ordinati per affinit\u00e0.\"\n              : (hasRankedBackend && !rankedConfig.isLoggedIn ? \"ACCEDI per giocare.\" : \"Premi INIZIA per giocare.\"));\n          elements.rankedResultsList.appendChild(empty);\n          return;\n        }\n\n        app.ranked.results.forEach((result) => {\n          elements.rankedResultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedSummary() {\n        elements.rankedSummaryPanel.classList.toggle(\"hidden\", !app.ranked.complete || !app.ranked.summaryLines.length);\n        elements.rankedSummaryPanel.classList.toggle(\"is-record\", app.ranked.isRecord);\n        elements.rankedSummaryTitle.textContent = app.ranked.summaryTitle || \"Partita conclusa\";\n        elements.rankedSummaryLines.innerHTML = \"\";\n\n        app.ranked.summaryLines.forEach((line) => {\n          const row = document.createElement(\"div\");\n          row.className = \"ranked-summary-line\";\n          row.innerHTML = line;\n          elements.rankedSummaryLines.appendChild(row);\n        });\n      }\n\n      function renderRankedPanel() {\n        renderRankedMeta();\n        renderRankedAttempts();\n        renderRankedInputState();\n        renderRankedActionButton();\n        renderRankedSummary();\n        renderRankedResults();\n        hideRankedSuggestions();\n      }\n\n      function renderApp() {\n        renderBranding();\n        renderTabs();\n        renderPanels();\n\n        if (app.mode === \"ranked\") {\n          renderRankedPanel();\n          hideSuggestions();\n          syncHelpToggleState();\n          return;\n        }\n\n        renderStandardAttempts();\n        renderStandardActionSlot();\n        renderStandardInputState();\n        renderStandardResults();\n        hideSuggestions();\n        hideRankedSuggestions();\n        syncHelpToggleState();\n      }\n\n      function formatTime(totalSeconds) {\n        const safeTime = Math.max(0, totalSeconds);\n        const minutes = Math.floor(safeTime \/ 60);\n        const seconds = String(safeTime % 60).padStart(2, \"0\");\n        return minutes + \":\" + seconds;\n      }\n\n      function getClassicSaveStatus() {\n        const state = app.modes.classic;\n        if (!state.complete) return \"playing\";\n        return state.results.some((result) => result.kind === \"guess\" && result.solved) ? \"won\" : \"lost\";\n      }\n\n      function saveClassicState(status) {\n        const state = app.modes.classic;\n        const payload = {\n          attempts: state.attempts,\n          guessedIds: Array.from(state.guessedIds),\n          guessedArtistIds: state.results.filter((result) => result.kind === \"guess\").map((result) => result.artist.id),\n          status: status || getClassicSaveStatus()\n        };\n\n        try {\n          window.localStorage.setItem(dailyStorageKey, JSON.stringify(payload));\n        } catch (_) {}\n      }\n\n      function loadClassicState() {\n        let rawState = \"\";\n\n        try {\n          rawState = window.localStorage.getItem(dailyStorageKey) || \"\";\n        } catch (_) {\n          return;\n        }\n\n        if (!rawState) return;\n\n        try {\n          const savedState = JSON.parse(rawState);\n          const state = app.modes.classic;\n          const savedArtistIds = Array.isArray(savedState.guessedArtistIds)\n            ? savedState.guessedArtistIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n            : [];\n          const restoredGuessedIds = new Set(\n            Array.isArray(savedState.guessedIds)\n              ? savedState.guessedIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n              : []\n          );\n\n          state.results = [];\n          state.guessedIds = restoredGuessedIds;\n          state.attempts = Math.max(0, Math.min(state.maxAttempts, Number(savedState.attempts || savedArtistIds.length)));\n          state.complete = false;\n\n          savedArtistIds.forEach((id, index) => {\n            const artist = artists.find((entry) => entry.id === id);\n            if (!artist) return;\n\n            state.guessedIds.add(artist.id);\n            state.results.push(buildResultEntry(artist, state.target, {\n              kind: \"guess\",\n              label: \"Tentativo \" + (index + 1)\n            }));\n          });\n\n          state.results.sort((left, right) => right.affinity - left.affinity);\n\n          if (savedState.status === \"won\") {\n            state.complete = true;\n            setMessage(\"Hai indovinato \" + state.target.name + \".\", \"success\");\n            syncDailyStreakResult(\"won\");\n            return;\n          }\n\n          if (savedState.status === \"lost\") {\n            state.complete = true;\n            state.results.unshift(buildResultEntry(state.target, state.target, {\n              kind: \"answer\",\n              label: \"Artista corretto\"\n            }));\n            setMessage(\"Tentativi finiti. L'artista era \" + state.target.name + \".\", \"error-final\");\n            syncDailyStreakResult(\"lost\");\n            return;\n          }\n\n          setMessage(\"\", \"\");\n        } catch (error) {\n          console.error(\"Errore nel caricamento del Daily Match salvato:\", error);\n          try {\n            window.localStorage.removeItem(dailyStorageKey);\n          } catch (_) {}\n        }\n      }\n\n      function addStandardGuess(guessArtist, modeKey) {\n        const state = app.modes[modeKey];\n        const targetArtist = state.target;\n        const attemptNumber = state.attempts + 1;\n        const isSolved = guessArtist.id === targetArtist.id;\n\n        state.guessedIds.add(guessArtist.id);\n        state.attempts += 1;\n        state.results.push(buildResultEntry(guessArtist, targetArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + attemptNumber\n        }));\n        state.results.sort((left, right) => right.affinity - left.affinity);\n\n        if (isSolved) {\n          state.complete = true;\n          setMessage(\"Hai indovinato \" + targetArtist.name + \".\", \"success\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"won\");\n            syncDailyStreakResult(\"won\");\n          }\n          return;\n        }\n\n        if (state.attempts >= state.maxAttempts) {\n          state.complete = true;\n          state.results.unshift(buildResultEntry(targetArtist, targetArtist, {\n            kind: \"answer\",\n            label: \"Artista corretto\"\n          }));\n          setMessage(\"Tentativi finiti. L'artista era \" + targetArtist.name + \".\", \"error-final\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"lost\");\n            syncDailyStreakResult(\"lost\");\n          }\n          return;\n        }\n\n        setMessage(\"\", \"\");\n        if (modeKey === \"classic\") {\n          saveClassicState(\"playing\");\n        }\n      }\n\n      function submitStandardGuess(modeKey) {\n        const state = app.modes[modeKey];\n        if (state.complete) return;\n\n        const guessArtist = findArtistByName(elements.input.value.trim());\n        if (!guessArtist) {\n          setMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (state.guessedIds.has(guessArtist.id)) {\n          setMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.input.value = \"\";\n          return;\n        }\n\n        addStandardGuess(guessArtist, modeKey);\n        elements.input.value = \"\";\n      }\n\n      function startPracticeRound() {\n        const nextRound = (app.modes.practice.round || 0) + 1;\n        app.modes.practice = {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: nextRound,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n        if (app.mode === \"practice\") {\n          setMessage(\"\", \"\");\n          renderApp();\n        }\n      }\n\n      function renderDailyStreakGuestState() {\n        elements.dailyStreakCard.classList.add(\"is-guest\");\n        elements.dailyStreakCard.setAttribute(\"href\", guestLoginUrl);\n        elements.dailyStreakCard.setAttribute(\"aria-label\", \"Accedi per salvare la streak\");\n        elements.dailyStreakMain.textContent = \"STREAK\";\n        elements.dailyStreakSub.textContent = \"Accedi per salvarla\";\n      }\n\n      function renderDailyStreakLoadingState() {\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        elements.dailyStreakMain.textContent = \"...\";\n        elements.dailyStreakSub.textContent = \"Caricamento streak\";\n      }\n\n      function renderDailyStreakState(data) {\n        if (!data || !data.is_logged_in) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        const streakValue = Number(data.current_streak || 0);\n        elements.dailyStreakMain.textContent = streakValue + \" \" + (streakValue === 1 ? \"giorno\" : \"giorni\");\n\n        if (data.today_result === \"won\") {\n          elements.dailyStreakSub.textContent = \"Indovinata oggi \u2022 Record \" + (data.best_streak || data.current_streak || 0);\n        } else if (data.today_result === \"lost\") {\n          elements.dailyStreakSub.textContent = \"Interrotta oggi \u2022 Record \" + (data.best_streak || 0);\n        } else {\n          elements.dailyStreakSub.textContent = \"Record \" + (data.best_streak || data.current_streak || 0);\n        }\n      }\n\n      async function postDailyStreakAction(action, extraData) {\n        const ajaxUrl = matchConfig.ajaxUrl;\n        if (!ajaxUrl) {\n          throw new Error(\"Config streak Match non trovata\");\n        }\n\n        const body = new URLSearchParams({\n          action: action,\n          nonce: matchConfig.nonce || \"\",\n          ...(extraData || {})\n        });\n\n        const response = await fetch(ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta streak Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      async function loadDailyStreakStatus(force) {\n        if (!matchConfig.isLoggedIn) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        if (dailyStreakLoading) return;\n        if (dailyStreakLoaded && !force) return;\n\n        dailyStreakLoading = true;\n        renderDailyStreakLoadingState();\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_status\");\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n            dailyStreakLoaded = true;\n          } else {\n            renderDailyStreakGuestState();\n          }\n        } catch (_) {\n          renderDailyStreakGuestState();\n        } finally {\n          dailyStreakLoading = false;\n        }\n      }\n\n      async function syncDailyStreakResult(result) {\n        if (!matchConfig.isLoggedIn) return;\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_update\", {\n            result: result,\n            day_key: italyDateInfo.dayKey\n          });\n\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n          }\n        } catch (error) {\n          console.error(\"Errore aggiornamento streak Bulldog Match:\", error);\n        }\n      }\n\n      async function postRankedAction(action, extraData) {\n        const body = new URLSearchParams({\n          action: action,\n          nonce: rankedConfig.nonce || \"\",\n          game_key: RANKED_GAME_KEY,\n          ...(extraData || {})\n        });\n\n        const response = await fetch(rankedConfig.ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta ranked Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      function getRankedLoginUrl() {\n        return rankedConfig.loginUrl || guestLoginUrl;\n      }\n\n      function resetRankedState() {\n        const state = app.ranked;\n        state.active = false;\n        state.preparing = false;\n        state.submitting = false;\n        state.complete = false;\n        state.betweenRounds = false;\n        state.sessionToken = \"\";\n        state.currentArtist = null;\n        state.score = 0;\n        state.timeLeft = RANKED_INITIAL_TIME;\n        state.roundAttempts = 0;\n        state.roundGuessedIds = new Set();\n        state.usedTargetIds = new Set();\n        state.freeHintArtist = null;\n        state.results = [];\n        state.summaryTitle = \"Partita conclusa\";\n        state.summaryLines = [];\n        state.isRecord = false;\n        state.localBest = readLocalRankedBest();\n        window.clearInterval(state.timerId);\n        window.clearTimeout(state.roundTransitionId);\n        state.timerId = 0;\n        state.roundTransitionId = 0;\n        setRankedMessage(\"\", \"\");\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n      }\n\n      function startNextRankedArtist() {\n        if (!app.ranked.active) return;\n\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.betweenRounds = false;\n\n        if (app.ranked.usedTargetIds.size >= artists.length) {\n          app.ranked.usedTargetIds.clear();\n        }\n\n        const nextArtist = pickRandomArtist(app.ranked.usedTargetIds);\n        app.ranked.currentArtist = nextArtist;\n        app.ranked.usedTargetIds.add(nextArtist.id);\n        app.ranked.roundAttempts = 0;\n        app.ranked.roundGuessedIds = new Set();\n        app.ranked.results = [];\n\n        if (app.ranked.freeHintArtist) {\n          app.ranked.results.unshift(buildResultEntry(app.ranked.freeHintArtist, app.ranked.currentArtist, {\n            kind: \"auto\",\n            label: \"Reveal artista passato\",\n            subcopy: \"Indizio gratis dal round precedente\",\n            ranked: true\n          }));\n          app.ranked.freeHintArtist = null;\n        }\n\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedPanel();\n        if (app.mode === \"ranked\") {\n          elements.rankedInput.focus();\n        }\n      }\n\n      function completeRankedRound(message, tone, carryArtist) {\n        if (!app.ranked.active || !app.ranked.currentArtist) return;\n        if (carryArtist) app.ranked.freeHintArtist = app.ranked.currentArtist;\n\n        app.ranked.betweenRounds = true;\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        renderRankedInputState();\n        renderRankedActionButton();\n        setRankedMessage(message, tone);\n\n        app.ranked.roundTransitionId = window.setTimeout(function () {\n          if (!app.ranked.active || app.ranked.timeLeft <= 0) return;\n          startNextRankedArtist();\n        }, RANKED_ROUND_TRANSITION_MS);\n      }\n\n      function tickRanked() {\n        if (!app.ranked.active) return;\n        app.ranked.timeLeft -= 1;\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n        }\n      }\n\n      async function startRankedGame() {\n        if (app.ranked.active || app.ranked.preparing) return;\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        app.ranked.preparing = true;\n        app.ranked.complete = false;\n        app.ranked.summaryLines = [];\n        renderRankedActionButton();\n\n        try {\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_start\");\n            if (!response || !response.success || !response.data || !response.data.is_logged_in) {\n              window.location.href = getRankedLoginUrl();\n              return;\n            }\n            app.ranked.sessionToken = String(response.data.token || \"\");\n          } else {\n            app.ranked.sessionToken = \"demo-\" + String(Date.now());\n          }\n\n          window.clearInterval(app.ranked.timerId);\n          window.clearTimeout(app.ranked.roundTransitionId);\n          app.ranked.active = true;\n          app.ranked.complete = false;\n          app.ranked.betweenRounds = false;\n          app.ranked.submitting = false;\n          app.ranked.score = 0;\n          app.ranked.timeLeft = RANKED_INITIAL_TIME;\n          app.ranked.roundAttempts = 0;\n          app.ranked.roundGuessedIds = new Set();\n          app.ranked.usedTargetIds = new Set();\n          app.ranked.freeHintArtist = null;\n          app.ranked.results = [];\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = [];\n          app.ranked.isRecord = false;\n          setRankedMessage(\"\", \"\");\n          renderRankedPanel();\n          startNextRankedArtist();\n          app.ranked.timerId = window.setInterval(tickRanked, 1000);\n        } catch (_) {\n          setRankedMessage(\"Non sono riuscito ad avviare la partita. Riprova tra poco.\", \"error\");\n        } finally {\n          app.ranked.preparing = false;\n          renderRankedInputState();\n          renderRankedActionButton();\n        }\n      }\n\n      function submitRankedGuess() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n\n        const guessArtist = findArtistByName(elements.rankedInput.value.trim());\n        if (!guessArtist) {\n          setRankedMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (app.ranked.roundGuessedIds.has(guessArtist.id)) {\n          setRankedMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.rankedInput.value = \"\";\n          return;\n        }\n\n        app.ranked.roundGuessedIds.add(guessArtist.id);\n        app.ranked.roundAttempts += 1;\n        app.ranked.results.push(buildResultEntry(guessArtist, app.ranked.currentArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + app.ranked.roundAttempts,\n          ranked: true\n        }));\n        app.ranked.results.sort((left, right) => right.affinity - left.affinity);\n        renderRankedAttempts();\n        renderRankedResults();\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n\n        if (guessArtist.id === app.ranked.currentArtist.id) {\n          app.ranked.score += 1;\n          app.ranked.timeLeft += RANKED_CORRECT_BONUS;\n          renderRankedMeta();\n          completeRankedRound(\"Hai indovinato!\", \"success\", true);\n          return;\n        }\n\n        if (app.ranked.roundAttempts >= RANKED_ATTEMPTS) {\n          completeRankedRound(\"Hai finito i tentativi! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n        }\n      }\n\n      function skipRankedArtist() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n        app.ranked.timeLeft = Math.max(0, app.ranked.timeLeft - RANKED_SKIP_PENALTY);\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n          return;\n        }\n        completeRankedRound(\"Hai skippato! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n      }\n\n      function escapeHtml(value) {\n        const div = document.createElement(\"div\");\n        div.textContent = value || \"\";\n        return div.innerHTML;\n      }\n\n      function buildRankedSummaryLines(data) {\n        const lines = [];\n        if (app.ranked.currentArtist && app.ranked.currentArtist.name) {\n          lines.push(\"L'ultimo artista era: <strong>\" + escapeHtml(app.ranked.currentArtist.name) + \"<\/strong>\");\n        }\n        lines.push(\"Punteggio finale: <strong>\" + String(app.ranked.score) + \"<\/strong>\");\n\n        if (data && typeof data.best_score !== \"undefined\") {\n          lines.push(\"Record mensile: <strong>\" + String(data.best_score) + \"<\/strong>\");\n          lines.push(\"Migliore posizione: <strong>\" + (data.best_rank ? \"#\" + data.best_rank : \"Fuori top 50\") + \"<\/strong>\");\n        } else {\n          const localBest = writeLocalRankedBest(app.ranked.score);\n          lines.push(\"Record demo: <strong>\" + String(localBest) + \"<\/strong>\");\n        }\n\n        return lines;\n      }\n\n      async function finishRankedGame() {\n        if (!app.ranked.active || app.ranked.submitting) return;\n\n        app.ranked.active = false;\n        app.ranked.complete = true;\n        app.ranked.betweenRounds = false;\n        app.ranked.submitting = true;\n        app.ranked.timeLeft = 0;\n        window.clearInterval(app.ranked.timerId);\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.timerId = 0;\n        app.ranked.roundTransitionId = 0;\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedMeta();\n        renderRankedInputState();\n        renderRankedActionButton();\n        app.ranked.summaryTitle = \"Partita conclusa\";\n        app.ranked.summaryLines = [\"Salvataggio record...\"];\n        app.ranked.isRecord = false;\n        renderRankedSummary();\n\n        try {\n          let data = null;\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_submit\", {\n              token: app.ranked.sessionToken,\n              score: String(app.ranked.score)\n            });\n            data = response && response.success ? response.data : null;\n          }\n\n          if (data) {\n            app.ranked.summaryTitle = data.is_record ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(data);\n            app.ranked.isRecord = Boolean(data.is_record);\n          } else {\n            const previousBest = readLocalRankedBest();\n            const nextBest = Math.max(previousBest, app.ranked.score);\n            writeLocalRankedBest(nextBest);\n            app.ranked.summaryTitle = nextBest === app.ranked.score && app.ranked.score > previousBest ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(null);\n            app.ranked.isRecord = app.ranked.score > previousBest;\n          }\n        } catch (_) {\n          const previousBest = readLocalRankedBest();\n          const nextBest = Math.max(previousBest, app.ranked.score);\n          writeLocalRankedBest(nextBest);\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = buildRankedSummaryLines(null);\n          app.ranked.isRecord = app.ranked.score > previousBest;\n        } finally {\n          app.ranked.submitting = false;\n          renderRankedPanel();\n        }\n      }\n\n      function buildSuggestionList(matches) {\n        elements.suggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.input.value = artist.name;\n            hideSuggestions();\n            elements.input.focus();\n          });\n          elements.suggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showSuggestions();\n          return;\n        }\n\n        hideSuggestions();\n      }\n\n      function updateSuggestions() {\n        const value = normalize(elements.input.value);\n        if (!value || getCurrentStandardState().complete) {\n          hideSuggestions();\n          return;\n        }\n\n        const guessedIds = getCurrentStandardState().guessedIds;\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !guessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        buildSuggestionList(matches);\n      }\n\n      function updateRankedSuggestions() {\n        const value = normalize(elements.rankedInput.value);\n        if (!value || !app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) {\n          hideRankedSuggestions();\n          return;\n        }\n\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !app.ranked.roundGuessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        elements.rankedSuggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.rankedInput.value = artist.name;\n            hideRankedSuggestions();\n            elements.rankedInput.focus();\n          });\n          elements.rankedSuggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showRankedSuggestions();\n          return;\n        }\n\n        hideRankedSuggestions();\n      }\n\n      function handleRankedSideAction() {\n        const action = elements.rankedSideActionButton.dataset.action;\n\n        if (action === \"login\") {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        if (action === \"start\" || action === \"restart\") {\n          startRankedGame();\n          return;\n        }\n\n        if (action === \"skip\") {\n          skipRankedArtist();\n        }\n      }\n\n      function switchMode(nextMode) {\n        if (nextMode === app.mode) return;\n\n        if (app.mode === \"ranked\" && nextMode !== \"ranked\") {\n          resetRankedState();\n        }\n\n        app.mode = nextMode;\n        elements.input.value = \"\";\n        elements.rankedInput.value = \"\";\n        hideSuggestions();\n        hideRankedSuggestions();\n        closeAllHelpPanels();\n        setMessage(\"\", \"\");\n        setRankedMessage(\"\", \"\");\n        renderApp();\n      }\n\n      elements.tabs.forEach((tab) => {\n        tab.addEventListener(\"click\", function () {\n          switchMode(tab.dataset.mode);\n        });\n      });\n\n      elements.rankedModeButton.addEventListener(\"click\", function () {\n        switchMode(\"ranked\");\n      });\n\n      if (elements.helpToggle) {\n        elements.helpToggle.addEventListener(\"click\", function () {\n          const panel = getCurrentHelpPanel();\n          if (!panel) return;\n\n          const shouldOpen = !panel.classList.contains(\"is-open\");\n          closeAllHelpPanels();\n\n          if (shouldOpen) {\n            panel.classList.add(\"is-open\");\n            panel.setAttribute(\"aria-hidden\", \"false\");\n          }\n\n          syncHelpToggleState();\n        });\n      }\n\n      elements.dailyStreakCard.addEventListener(\"click\", function (event) {\n        if (elements.dailyStreakCard.classList.contains(\"is-guest\")) {\n          event.preventDefault();\n          window.location.href = elements.dailyStreakCard.getAttribute(\"href\") || guestLoginUrl;\n          return;\n        }\n\n        event.preventDefault();\n        event.stopPropagation();\n      });\n\n      elements.modeActionButton.addEventListener(\"click\", function () {\n        if (app.mode === \"practice\") {\n          startPracticeRound();\n        }\n      });\n\n      elements.guessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitStandardGuess(app.mode === \"classic\" ? \"classic\" : \"practice\");\n        renderApp();\n      });\n\n      elements.rankedGuessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitRankedGuess();\n        renderRankedPanel();\n      });\n\n      elements.rankedSideActionButton.addEventListener(\"click\", function () {\n        handleRankedSideAction();\n      });\n\n      elements.input.addEventListener(\"input\", updateSuggestions);\n      elements.input.addEventListener(\"focus\", updateSuggestions);\n      elements.rankedInput.addEventListener(\"input\", updateRankedSuggestions);\n      elements.rankedInput.addEventListener(\"focus\", updateRankedSuggestions);\n\n      document.addEventListener(\"click\", function (event) {\n        if (!elements.suggestions.contains(event.target) && event.target !== elements.input) {\n          hideSuggestions();\n        }\n\n        if (!elements.rankedSuggestions.contains(event.target) && event.target !== elements.rankedInput) {\n          hideRankedSuggestions();\n        }\n      });\n\n      window.addEventListener(\"resize\", function () {\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      });\n\n      window.addEventListener(\"scroll\", function () {\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      }, true);\n\n      if (window.visualViewport) {\n        window.visualViewport.addEventListener(\"resize\", function () {\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n\n        window.visualViewport.addEventListener(\"scroll\", function () {\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n      }\n\n      loadClassicState();\n      renderApp();\n      if (!app.modes.classic.complete) {\n        setMessage(\"\", \"\");\n      }\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-41e52d73 elementor-widget elementor-widget-html\" data-id=\"41e52d73\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"ranked-board-widget ranked-sidebar-board\" data-game-key=\"match\">\n  <div class=\"ranked-sidebar-board-inner\">\n    <div class=\"ranked-board-controls\" aria-label=\"Seleziona mese classifica\">\n      <select class=\"ranked-board-period-select\">\n        <option value=\"\">Caricamento...<\/option>\n      <\/select>\n    <\/div>\n\n    <img decoding=\"async\" class=\"ranked-sidebar-board-heading-image\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/LeadBasico.png\" alt=\"Classifica\">\n\n    <div class=\"ranked-board-list\"><\/div>\n\n    <div class=\"ranked-board-pager\">\n      <button class=\"ranked-board-prev\" type=\"button\" aria-label=\"Pagina precedente\">&#8249;<\/button>\n      <span class=\"ranked-board-page\">1\/1<\/span>\n      <button class=\"ranked-board-next\" type=\"button\" aria-label=\"Pagina successiva\">&#8250;<\/button>\n    <\/div>\n\n    <div class=\"ranked-board-msg\">Caricamento classifica...<\/div>\n  <\/div>\n<\/div>\n\n<script src=\"\/bulldogle\/ranked-leaderboard.js?v=58\"><\/script>\n\n<style>\n.ranked-sidebar-board {\n  --ranked-accent: #952800;\n  --ranked-accent-2: #a83100;\n  --ranked-accent-3: #7d2100;\n  --ranked-ink: #17110d;\n  --ranked-ink-soft: rgba(23, 17, 13, 0.72);\n  --ranked-cream: #f8f0e8;\n  width: 100%;\n  max-width: 100%;\n  margin: 0;\n  font-family: Verdana, Geneva, sans-serif;\n}\n\n.ranked-sidebar-board-inner {\n  position: relative;\n  width: 100%;\n  padding: 22px 22px 20px;\n  border-radius: 34px;\n  overflow: hidden;\n  background: var(--ranked-accent);\n  box-shadow:\n    inset 0 1px 0 rgba(255, 255, 255, 0.16),\n    0 18px 34px rgba(0, 0, 0, 0.18);\n  color: var(--ranked-ink);\n  text-align: center;\n}\n\n.ranked-sidebar-board-heading-image {\n  display: block;\n  width: min(100%, 206px) !important;\n  height: auto;\n  margin: 2px auto 16px;\n}\n.ranked-sidebar-board .ranked-board-controls {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-bottom: 12px;\n}\n\n.ranked-sidebar-board .ranked-board-controls select {\n  width: 100% !important;\n  max-width: 182px;\n  min-width: 0;\n  height: 40px;\n  padding: 0 14px !important;\n  border: 1px solid rgba(23, 17, 13, 0.12) !important;\n  border-radius: 999px !important;\n  background: rgba(12, 8, 6, 0.9) !important;\n  color: var(--ranked-cream) !important;\n  font-family: Verdana, Geneva, sans-serif;\n  font-size: 12px !important;\n  font-weight: 700;\n  text-align: center;\n  box-shadow: none !important;\n  outline: none;\n  appearance: none;\n  -webkit-appearance: none;\n}\n\n.ranked-sidebar-board .ranked-board-list {\n  display: grid;\n  gap: 10px;\n  margin: 0;\n}\n\n.ranked-sidebar-board .ranked-board-row {\n  display: grid;\n  grid-template-columns: 66px minmax(0, 1fr) 64px;\n  align-items: center;\n  gap: 8px;\n  min-height: 52px;\n  padding: 10px 14px;\n  border: 0;\n  border-radius: 18px;\n  background: #000000;\n  color: var(--ranked-cream);\n  text-align: center;\n}\n\n.ranked-sidebar-board .ranked-board-row:first-child {\n  background: #000000;\n}\n\n.ranked-sidebar-board .ranked-board-rank {\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-name {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-score {\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-pager {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 12px;\n  margin-top: 14px;\n}\n\n.ranked-sidebar-board .ranked-board-pager button {\n  appearance: none !important;\n  display: inline-flex !important;\n  align-items: center !important;\n  justify-content: center !important;\n  width: 36px !important;\n  min-width: 36px !important;\n  height: 36px !important;\n  min-height: 36px !important;\n  padding: 0 !important;\n  margin: 0 !important;\n  border: 1px solid rgba(23, 17, 13, 0.12) !important;\n  border-radius: 999px !important;\n  background: #000000 !important;\n  color: #ffffff !important;\n  font-family: Verdana, Geneva, sans-serif !important;\n  font-size: 23px !important;\n  font-weight: 700 !important;\n  line-height: 1 !important;\n  box-shadow: none !important;\n  transform: none !important;\n  cursor: pointer;\n}\n\n.ranked-sidebar-board .ranked-board-pager button:hover {\n  background: #ffffff !important;\n  color: var(--ranked-ink) !important;\n}\n\n.ranked-sidebar-board .ranked-board-pager button:disabled {\n  opacity: 0.34;\n  cursor: not-allowed;\n}\n\n.ranked-sidebar-board .ranked-board-page {\n  color: rgba(23, 17, 13, 0.72);\n  font-size: 12px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-msg {\n  min-height: 18px;\n  margin-top: 12px;\n  color: rgba(23, 17, 13, 0.72);\n  font-size: 12px;\n  line-height: 1.35;\n  font-weight: 700;\n  text-align: center;\n}\n\n@media (max-width: 767px) {\n  .ranked-sidebar-board-inner {\n    padding: 20px 18px 18px;\n    border-radius: 28px;\n  }\n\n  .ranked-sidebar-board .ranked-board-row {\n    grid-template-columns: 58px minmax(0, 1fr) 58px;\n    min-height: 48px;\n    padding: 9px 12px;\n  }\n\n  .ranked-sidebar-board-heading-image {\n    width: min(100%, 238px);\n  }\n}\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-6eb0864c elementor-hidden-desktop elementor-hidden-tablet e-flex e-con-boxed wcf-starter-animations-none e-con e-parent\" data-id=\"6eb0864c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9825ae8 show-when-ranked wcf-starter-animations-none elementor-widget elementor-widget-image\" data-id=\"9825ae8\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;wcf_starter_animations&quot;:&quot;none&quot;,&quot;wcf_anim_duration&quot;:1000,&quot;wcf_anim_delay&quot;:0,&quot;wcf_anim_ease&quot;:&quot;ease&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1-1024x576.png\" class=\"attachment-large size-large wp-image-14975\" alt=\"\" srcset=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1-1024x576.png 1024w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1-300x169.png 300w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1-768x432.png 768w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1-1320x742.png 1320w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1.png 1366w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-29988ab hide-when-ranked wcf-starter-animations-none elementor-widget elementor-widget-image\" data-id=\"29988ab\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;wcf_starter_animations&quot;:&quot;none&quot;,&quot;wcf_anim_duration&quot;:1000,&quot;wcf_anim_delay&quot;:0,&quot;wcf_anim_ease&quot;:&quot;ease&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New-1024x576.png\" class=\"attachment-large size-large wp-image-14974\" alt=\"\" srcset=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New-1024x576.png 1024w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New-300x169.png 300w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New-768x432.png 768w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New-1320x742.png 1320w, https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png 1366w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c9fb1bf elementor-widget elementor-widget-wcf--floating-elements\" data-id=\"c9fb1bf\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"wcf--floating-elements.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"wcf--floating-elements\">\r\n\t\t\t                <div class=\"floating-element wcf-live-anim-wiggle elementor-repeater-item-d4c5f46   \">\r\n\t\t\t\t\t<img decoding=\"async\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Coppa.png\" alt=\"Coppa\" \/>\t\t\t\t<\/div>\r\n\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5db8e5f elementor-widget-divider--view-line_text elementor-widget-mobile__width-initial elementor-widget-divider--element-align-center elementor-widget elementor-widget-divider\" data-id=\"5db8e5f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"divider.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-divider\">\n\t\t\t<span class=\"elementor-divider-separator\">\n\t\t\t\t\t\t\t<span class=\"elementor-divider__text elementor-divider__element\">\n\t\t\t\tModalit\u00e0\t\t\t\t<\/span>\n\t\t\t\t\t\t<\/span>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4143ea45 elementor-widget-mobile__width-initial elementor-widget elementor-widget-html\" data-id=\"4143ea45\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"it\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>BulldogMatch Demo<\/title>\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Instrument+Sans:wght@400;500;600;700;800&family=Space+Grotesk:wght@500;700&display=swap\" rel=\"stylesheet\">\n  <style>\n    :root {\n      --bg: #000;\n      --panel: #070707;\n      --panel-2: #0b0b0b;\n      --line: rgba(255, 255, 255, 0.08);\n      --line-faint: rgba(255, 255, 255, 0.05);\n      --line-strong: rgba(241, 108, 29, 0.28);\n      --ranked-line-strong: rgba(149, 40, 0, 0.28);\n      --white: #f7f4ef;\n      --copy: rgba(247, 244, 239, 0.76);\n      --copy-soft: rgba(247, 244, 239, 0.56);\n      --copy-faint: rgba(247, 244, 239, 0.34);\n      --accent: #f16c1d;\n      --ranked-accent: #952800;\n      --success: #7dc56a;\n      --warning: #f0b955;\n      --danger: #ff7e59;\n      --ease: 220ms cubic-bezier(.2,.9,.2,1);\n    }\n\n    * {\n      box-sizing: border-box;\n    }\n\n    html,\n    body {\n      margin: 0;\n      min-height: 100%;\n      background: var(--bg);\n      color: var(--white);\n      font-family: \"Instrument Sans\", system-ui, sans-serif;\n      overflow-x: hidden;\n    }\n\n    button,\n    input,\n    iframe {\n      font: inherit;\n    }\n\n    .demo-app {\n      width: min(760px, calc(100vw - 20px));\n      margin: 0 auto;\n      padding: 18px 0 24px;\n    }\n\n    .app-shell {\n      position: relative;\n      isolation: isolate;\n      padding: 14px;\n      border: 1px solid var(--line);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.98), rgba(7, 7, 7, 0.98));\n      overflow: visible;\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n    }\n\n    .app-shell::after {\n      content: \"\";\n      position: absolute;\n      inset: 0;\n      border-radius: inherit;\n      background-image: url(\"data:image\/svg+xml,%3Csvg%20xmlns='http:\/\/www.w3.org\/2000\/svg'%20viewBox='0%200%20180%20180'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='.9'%20numOctaves='3'%20stitchTiles='stitch'\/%3E%3CfeColorMatrix%20type='saturate'%20values='0'\/%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type='table'%20tableValues='0%20.58'\/%3E%3C\/feComponentTransfer%3E%3C\/filter%3E%3Crect%20width='180'%20height='180'%20filter='url(%23n)'%20opacity='1'\/%3E%3C\/svg%3E\");\n      background-size: 180px 180px;\n      mix-blend-mode: overlay;\n      opacity: 0.1;\n      pointer-events: none;\n    }\n\n    .app-shell > * {\n      position: relative;\n      z-index: 1;\n    }\n\n    .topbar {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 0;\n      padding: 10px 8px 14px;\n    }\n\n        .top-logo {\n      display: block;\n      width: min(320px, 58vw);\n      height: auto;\n    }\n    \n    @media (max-width: 768px) {\n  .top-logo {\n    display: none !important;\n  } }\n\n\n    .controls-shell {\n      margin-top: 0;\n      padding: 14px 0 8px;\n      border-top: 1px solid var(--line);\n    }\n\n    .controls-inner {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 8px;\n    }\n\n    .tab-ranked-mobile {\n      display: none !important;\n    }\n\n    .shell {\n      margin-top: 8px;\n      padding-top: 14px;\n      border-top: 1px solid var(--line);\n    }\n\n    .game-shell {\n      position: relative;\n      padding: 2px 0 6px;\n      overflow: visible;\n    }\n\n    .tab,\n    .ghost-button,\n    .button,\n    .secondary-button {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-width: 0;\n      min-height: 36px;\n      padding: 0 10px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--white);\n      font-size: 12px;\n      font-weight: 700;\n      cursor: pointer;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .tab.is-active,\n    .ghost-button.is-active {\n      border-color: var(--line-strong);\n      background: rgba(241, 108, 29, 0.12);\n    }\n\n    .demo-app.is-ranked-mode #rankedModeButton.is-active,\n    .demo-app.is-ranked-mode .ghost-button.is-active {\n      border-color: var(--ranked-line-strong);\n      background: rgba(149, 40, 0, 0.12);\n    }\n\n    .demo-app.is-ranked-mode .hide-when-ranked,\n    body.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    html.bulldogmatch-ranked-mode-active .hide-when-ranked,\n    .demo-app.is-ranked-mode .hide.when.ranked,\n    body.bulldogmatch-ranked-mode-active .hide.when.ranked,\n    html.bulldogmatch-ranked-mode-active .hide.when.ranked {\n      display: none !important;\n    }\n\n    .show-when-ranked,\n    .show.when.ranked,\n    .show-when-ranked-flex,\n    .show.flex.when.ranked,\n    .show-when-ranked-grid,\n    .show.grid.when.ranked,\n    .show-when-ranked-inline {\n      display: none !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked,\n    .demo-app.is-ranked-mode .show.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.when.ranked {\n      display: block !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-flex,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-flex,\n    .demo-app.is-ranked-mode .show.flex.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.flex.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.flex.when.ranked {\n      display: flex !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-grid,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-grid,\n    .demo-app.is-ranked-mode .show.grid.when.ranked,\n    body.bulldogmatch-ranked-mode-active .show.grid.when.ranked,\n    html.bulldogmatch-ranked-mode-active .show.grid.when.ranked {\n      display: grid !important;\n    }\n\n    .demo-app.is-ranked-mode .show-when-ranked-inline,\n    body.bulldogmatch-ranked-mode-active .show-when-ranked-inline,\n    html.bulldogmatch-ranked-mode-active .show-when-ranked-inline {\n      display: inline-flex !important;\n    }\n\n    .button,\n    .secondary-button,\n    .attempts-card {\n      min-width: 152px;\n      min-height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n      font-variation-settings: \"wght\" 700;\n      box-shadow: none;\n    }\n\n    .button,\n    .secondary-button.is-primary {\n      background: var(--accent);\n      border-color: var(--accent);\n      color: #111;\n    }\n\n    .secondary-button.is-muted {\n      background: rgba(255, 255, 255, 0.03);\n      border-color: var(--line);\n      color: var(--white);\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-form .button,\n    .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary {\n      background: var(--ranked-accent);\n      border-color: var(--ranked-accent);\n      color: #111;\n    }\n\n    .button:hover,\n    .secondary-button:hover {\n      background: #fff;\n      border-color: #fff;\n      color: #111;\n    }\n\n    .tab:hover,\n    .ghost-button:hover,\n    .button:hover,\n    .secondary-button:hover,\n    .streak-card.is-guest:hover,\n    .suggestion:hover {\n      transform: translateY(-1px);\n    }\n\n    .button:disabled,\n    .secondary-button:disabled {\n      cursor: not-allowed;\n      opacity: 0.45;\n      transform: none;\n    }\n\n    .help-panel {\n      max-height: 0;\n      overflow: hidden;\n      padding-bottom: 0;\n      border-bottom: 0;\n      transition: max-height 240ms ease, margin 240ms ease, padding-bottom 240ms ease, border-color 240ms ease;\n      margin: 0;\n    }\n\n    .help-panel.is-open {\n      max-height: 1200px;\n      padding-bottom: 12px;\n      border-bottom: 1px solid var(--line);\n      margin: 0 0 12px;\n    }\n\n    .help-panel-mobile-slot {\n      display: none;\n    }\n\n    .help-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n      gap: 10px;\n    }\n\n    .help-card {\n      min-height: 100%;\n      padding: 12px 13px;\n      border: 1px solid var(--line);\n      border-radius: 12px;\n      background: rgba(255, 255, 255, 0.03);\n    }\n\n    .help-card strong {\n      display: block;\n      margin-bottom: 8px;\n      font-size: 13px;\n    }\n\n    .help-card span {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-standard-panel,\n    .match-ranked-panel {\n      display: grid;\n      gap: 12px;\n    }\n\n    .match-ranked-panel.hidden,\n    .match-standard-panel.hidden,\n    .help-panel.hidden,\n    .hidden {\n      display: none !important;\n    }\n\n    .match-meta-grid {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 10px;\n      margin-bottom: 2px;\n    }\n\n    .match-meta-card {\n      min-height: 74px;\n      padding: 12px 14px;\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      background: rgba(255, 255, 255, 0.03);\n      display: flex;\n      flex-direction: column;\n      justify-content: space-between;\n      gap: 8px;\n      text-align: left;\n    }\n\n    .match-meta-grid > .match-meta-card,\n    .match-meta-grid > .ranked-meta-button {\n      align-self: stretch;\n      height: auto;\n    }\n\n    .match-meta-card.is-accent {\n      border-color: rgba(149, 40, 0, 0.24);\n      background: rgba(149, 40, 0, 0.08);\n    }\n\n    .match-meta-label {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.12em;\n      text-transform: uppercase;\n    }\n\n    .match-meta-value {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(24px, 3.2vw, 30px);\n      line-height: 1;\n      letter-spacing: -0.05em;\n    }\n\n    .input-grid {\n      display: grid;\n      grid-template-columns: 152px minmax(0, 1fr) 152px 152px;\n      gap: 10px;\n      align-items: stretch;\n      width: 100%;\n      margin: 0 auto;\n    }\n\n    .attempts-card {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      padding: 0 22px;\n      border: 1px solid #fff;\n      border-radius: 12px;\n      background: #fff;\n      color: #111;\n      text-align: center;\n      white-space: nowrap;\n    }\n\n    .action-slot {\n      width: 152px;\n      min-width: 152px;\n      align-self: stretch;\n    }\n\n    .input-grid > .attempts-card,\n    .input-grid > .button,\n    .action-slot > .secondary-button,\n    .action-slot > .streak-card {\n      width: 100%;\n      min-width: 0;\n      min-height: 56px;\n      height: 56px;\n    }\n\n    .input-grid > .action-slot {\n      align-self: stretch;\n      height: 56px;\n    }\n\n    .input-shell {\n      position: relative;\n      z-index: 5;\n      min-width: 0;\n    }\n\n    .input-label {\n      position: absolute;\n      width: 1px;\n      height: 1px;\n      padding: 0;\n      margin: -1px;\n      overflow: hidden;\n      clip: rect(0, 0, 0, 0);\n      white-space: nowrap;\n      border: 0;\n    }\n\n    .search-field {\n      width: 100%;\n      min-height: 56px;\n      padding: 0 16px;\n      border-radius: 12px;\n      border: 1px solid rgba(255, 255, 255, 0.1);\n      background: #101010;\n      color: var(--white);\n      font-size: 15px;\n      outline: none;\n      transition: border-color var(--ease), background var(--ease), opacity var(--ease);\n    }\n\n    .search-field:focus {\n      border-color: rgba(255, 255, 255, 0.18);\n      background: #111;\n    }\n\n    .search-field::placeholder {\n      color: var(--copy-faint);\n    }\n\n    .search-field:disabled {\n      opacity: 0.5;\n      cursor: not-allowed;\n    }\n\n    .suggestions {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 280px;\n      max-height: min(280px, calc(100dvh - 24px));\n      overflow-y: auto;\n      z-index: 2147483001;\n      display: none;\n      padding: 6px;\n      border: 1px solid var(--line);\n      border-radius: 14px;\n      background: rgba(9, 9, 9, 0.98);\n      box-shadow: 0 22px 50px rgba(0, 0, 0, 0.55);\n      backdrop-filter: blur(12px);\n      overscroll-behavior: contain;\n      -webkit-overflow-scrolling: touch;\n    }\n\n    .suggestion {\n      width: 100%;\n      display: flex;\n      justify-content: space-between;\n      gap: 8px;\n      padding: 11px 12px;\n      border: 0;\n      border-radius: 10px;\n      background: transparent;\n      color: var(--white);\n      text-align: left;\n      cursor: pointer;\n    }\n\n    .suggestion small {\n      color: var(--copy-faint);\n      font-size: 11px;\n    }\n\n    .streak-card {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 100%;\n      min-height: 56px;\n      height: 56px;\n      padding: 0 22px;\n      border-radius: 12px;\n      border: 1px solid var(--line);\n      background: var(--accent);\n      color: #111;\n      text-decoration: none;\n      text-align: center;\n      cursor: default;\n      transition: transform var(--ease), border-color var(--ease), background var(--ease), color var(--ease), opacity var(--ease);\n    }\n\n    .streak-card.is-guest {\n      cursor: pointer;\n    }\n\n    .streak-copy {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      justify-content: center;\n      gap: 3px;\n      min-width: 0;\n    }\n\n    .streak-main {\n      display: block;\n      font-size: 15px;\n      line-height: 1;\n      font-weight: 700;\n      letter-spacing: 0;\n      text-transform: uppercase;\n    }\n\n    .streak-sub {\n      display: block;\n      color: rgba(17, 17, 17, 0.74);\n      font-size: 10px;\n      line-height: 1.1;\n      font-weight: 700;\n      letter-spacing: 0;\n      white-space: nowrap;\n    }\n\n    .message {\n      display: none;\n      width: 100%;\n      margin: 10px 0 0;\n      padding: 10px 12px;\n      border-radius: 999px;\n      border: 1px solid var(--line);\n      background: rgba(255, 255, 255, 0.03);\n      color: var(--copy);\n      font-size: 13px;\n      line-height: 1.45;\n      text-align: center;\n    }\n\n    .message.is-visible {\n      display: block;\n    }\n\n    .message.is-success {\n      border-color: rgba(125, 197, 106, 0.36);\n      background: rgba(125, 197, 106, 0.1);\n      color: #daf2d3;\n    }\n\n    .message.is-error {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .message.is-error-final {\n      border-color: rgba(241, 108, 29, 0.32);\n      background: rgba(241, 108, 29, 0.1);\n      color: #ffd7b5;\n    }\n\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error,\n    .demo-app.is-ranked-mode .match-ranked-panel .message.is-error-final {\n      border-color: rgba(149, 40, 0, 0.32);\n      background: rgba(149, 40, 0, 0.1);\n      color: #ffd7b5;\n    }\n\n    .results-list {\n      display: grid;\n      gap: 12px;\n      margin-top: 12px;\n      width: 100%;\n    }\n\n    .empty-state {\n      padding: 16px;\n      border: 1px dashed rgba(255, 255, 255, 0.15);\n      border-radius: 12px;\n      color: var(--copy-soft);\n      font-size: 13px;\n      line-height: 1.5;\n      text-align: center;\n    }\n\n    .match-card {\n      padding: 18px;\n      border: 1px solid var(--line-faint);\n      border-radius: 22px;\n      background: linear-gradient(180deg, rgba(14, 14, 14, 0.96), rgba(10, 10, 10, 0.96));\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n      text-align: left;\n    }\n\n    .match-card.is-solved {\n      border-color: rgba(125, 197, 106, 0.34);\n    }\n\n    .match-card.is-answer {\n      border-color: rgba(241, 108, 29, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-card.is-answer {\n      border-color: rgba(149, 40, 0, 0.34);\n    }\n\n    .match-card.is-auto {\n      border-color: rgba(255, 255, 255, 0.16);\n    }\n\n    .match-top {\n      display: grid;\n      grid-template-columns: 92px minmax(0, 1fr) auto;\n      gap: 16px;\n      align-items: center;\n      margin-bottom: 14px;\n    }\n\n    .match-cover {\n      position: relative;\n      width: 92px;\n      height: 92px;\n      border: 1px solid rgba(255, 255, 255, 0.12);\n      border-radius: 50%;\n      background:\n        radial-gradient(circle at 32% 28%, rgba(241, 108, 29, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(241, 108, 29, 0.16), rgba(255, 255, 255, 0.03));\n      overflow: hidden;\n      box-shadow: 0 18px 34px rgba(0, 0, 0, 0.34);\n    }\n\n    .demo-app.is-ranked-mode .match-cover {\n      background:\n        radial-gradient(circle at 32% 28%, rgba(149, 40, 0, 0.34), transparent 44%),\n        linear-gradient(145deg, rgba(149, 40, 0, 0.16), rgba(255, 255, 255, 0.03));\n    }\n\n    .match-cover img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n\n    .match-cover-fallback {\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: 28px;\n      font-weight: 700;\n      letter-spacing: -0.04em;\n      text-transform: uppercase;\n    }\n\n    .match-copy {\n      min-width: 0;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-start;\n      gap: 10px;\n    }\n\n    .match-badge {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-height: 28px;\n      padding: 0 12px;\n      border: 1px solid var(--line);\n      border-radius: 999px;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 700;\n      letter-spacing: -0.01em;\n    }\n\n    .match-name {\n      margin: 0;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(28px, 4vw, 34px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n    }\n\n    .match-sub {\n      margin: -2px 0 0;\n      color: var(--copy-soft);\n      font-size: 12px;\n      line-height: 1.45;\n    }\n\n    .match-score {\n      min-width: 112px;\n      display: flex;\n      flex-direction: column;\n      align-items: flex-end;\n      gap: 4px;\n      text-align: right;\n    }\n\n    .match-score-main {\n      display: block;\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(30px, 4.6vw, 38px);\n      line-height: 1;\n      letter-spacing: -0.06em;\n    }\n\n    .match-score-sub {\n      display: block;\n      color: var(--copy-soft);\n      font-size: 11px;\n      font-weight: 800;\n      letter-spacing: 0.14em;\n      text-transform: uppercase;\n    }\n\n    .match-meter {\n      position: relative;\n      height: 16px;\n      border-radius: 999px;\n      background: rgba(255, 255, 255, 0.06);\n      overflow: hidden;\n    }\n\n    .match-meter-fill {\n      position: absolute;\n      inset: 0 auto 0 0;\n      width: 0;\n      border-radius: inherit;\n      transition: width 320ms ease;\n    }\n\n    .match-spotify {\n      margin-top: 12px;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n    }\n\n    .match-spotify iframe {\n      width: 100%;\n      min-height: 152px;\n      border: 0;\n      border-radius: 14px;\n      background: #0b0b0b;\n      display: block;\n    }\n\n    .match-tip {\n      margin: 12px 0 0;\n      padding-top: 12px;\n      border-top: 1px solid rgba(255, 255, 255, 0.06);\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.55;\n    }\n\n    .ranked-summary-panel {\n      margin-top: 4px;\n      padding: 16px;\n      border: 1px solid rgba(149, 40, 0, 0.28);\n      border-radius: 18px;\n      background: rgba(149, 40, 0, 0.08);\n      width: 100%;\n    }\n\n    .ranked-summary-panel.hidden {\n      display: none !important;\n    }\n\n    .ranked-summary-panel.is-record {\n      border-color: rgba(125, 197, 106, 0.32);\n      background: rgba(125, 197, 106, 0.08);\n    }\n\n    .ranked-summary-title {\n      margin: 0 0 10px;\n      color: var(--white);\n      font-family: \"Space Grotesk\", sans-serif;\n      font-size: clamp(22px, 4vw, 28px);\n      line-height: 1;\n      letter-spacing: -0.04em;\n      font-weight: 900;\n    }\n\n    .ranked-summary-lines {\n      display: grid;\n      gap: 5px;\n    }\n\n    .ranked-summary-line {\n      color: var(--white);\n      font-size: 14px;\n      line-height: 1.5;\n      font-weight: 600;\n    }\n\n    .ranked-summary-line strong {\n      font-weight: 800;\n    }\n\n    @media (max-width: 720px) {\n      .demo-app {\n        width: 100%;\n        max-width: 100%;\n        padding: max(6px, env(safe-area-inset-top, 0px)) 4px calc(10px + env(safe-area-inset-bottom, 0px));\n      }\n\n      .app-shell {\n        display: flex;\n        flex-direction: column;\n        padding: 8px 6px 10px;\n        border: 0;\n        border-radius: 0;\n        background: transparent;\n        box-shadow: none;\n      }\n\n      .app-shell::after {\n        display: none;\n      }\n\n      .controls-shell {\n        order: 1;\n        position: relative;\n        border-top: 0;\n        border-bottom: 1px solid var(--line);\n        padding-top: 10px;\n        padding-bottom: 20px;\n      }\n\n      .controls-shell::after {\n        content: \"Gioca\";\n        position: absolute;\n        left: 50%;\n        bottom: -11px;\n        transform: translateX(-50%) translateY(-2px);\n        padding: 0 14px;\n        background: var(--bg);\n        color: #9E9E9E;\n        font-size: 15px;\n        line-height: 1;\n      }\n\n      .controls-inner {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .tab-ranked-mobile {\n        display: inline-flex !important;\n      }\n\n      .help-panel-mobile-slot {\n        display: block;\n      }\n\n      .topbar {\n        order: 2;\n        padding: 12px 4px 4px;\n      }\n\n      .top-logo {\n        width: min(260px, 66vw);\n      }\n\n      .shell {\n        order: 3;\n        border-top: 0;\n        padding-top: 14px;\n      }\n\n      .input-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n        width: 100%;\n      }\n\n      .attempts-card {\n        order: 1;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n        padding: 0 10px;\n        font-size: 13px;\n      }\n\n      .action-slot {\n        order: 2;\n        width: auto;\n        min-width: 0;\n      }\n\n      .input-shell {\n        order: 3;\n        grid-column: 1 \/ -1;\n        padding: 0;\n        border: 0;\n        border-radius: 0;\n        background: #0b0b0b;\n      }\n\n      .input-grid > .button {\n        order: 4;\n        grid-column: 1 \/ -1;\n      }\n\n      .input-grid > .attempts-card,\n      .input-grid > .button,\n      .action-slot > .secondary-button,\n      .action-slot > .streak-card,\n      .input-grid > .action-slot {\n        width: 100%;\n        min-width: 0;\n        min-height: 48px;\n        height: 48px;\n      }\n\n      .button,\n      .secondary-button,\n      .streak-card,\n      .attempts-card,\n      .action-slot {\n        min-width: 0;\n      }\n\n      .search-field {\n        min-height: 48px;\n        padding: 0 14px;\n        border: 1px solid rgba(255, 255, 255, 0.14);\n        border-radius: 12px;\n        background: #0b0b0b;\n        font-size: 16px;\n      }\n\n      .suggestion {\n        min-height: 48px;\n        padding: 13px 12px;\n      }\n\n      .help-grid {\n        grid-template-columns: 1fr;\n      }\n\n      .controls-shell .help-panel.is-open {\n        margin: 10px 0 0;\n        padding-bottom: 0;\n        border-bottom-color: transparent;\n      }\n\n      .help-card span {\n        font-size: 13px;\n        line-height: 1.5;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 8px;\n      }\n\n      .match-meta-grid > .match-meta-card:last-child {\n        grid-column: 1 \/ -1;\n      }\n\n      .demo-app.is-ranked-mode #rankedGamePanel .match-meta-grid {\n        margin-bottom: 0;\n        padding-bottom: 0;\n        border-bottom: 0;\n      }\n\n      .demo-app.is-ranked-mode #rankedGamePanel .match-meta-grid > .match-meta-card:last-child {\n        display: none;\n      }\n\n      .match-meta-card {\n        min-height: 62px;\n        padding: 10px 11px;\n      }\n\n      .match-meta-value {\n        font-size: clamp(18px, 5vw, 24px);\n      }\n\n      .match-top {\n        grid-template-columns: 72px minmax(0, 1fr);\n        gap: 12px;\n      }\n\n      .match-score {\n        grid-column: 1 \/ -1;\n        min-width: 0;\n        align-items: flex-start;\n        text-align: left;\n      }\n\n      .match-copy {\n        gap: 8px;\n      }\n\n      .match-card {\n        padding: 12px;\n        border-radius: 16px;\n      }\n\n      .match-cover {\n        width: 72px;\n        height: 72px;\n      }\n\n      .match-name {\n        font-size: clamp(20px, 7vw, 26px);\n      }\n\n      .match-score-main {\n        font-size: 28px;\n      }\n\n      .message {\n        margin-top: 10px;\n        padding: 9px 11px;\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 520px) {\n      .demo-app {\n        padding-inline: 3px;\n      }\n\n      .controls-shell {\n        padding-top: 8px;\n      }\n\n      .top-logo {\n        width: min(232px, 64vw);\n      }\n\n      .shell {\n        padding-top: 8px;\n      }\n\n      .controls-inner {\n        grid-template-columns: 1fr;\n      }\n\n      .match-meta-grid {\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n      }\n\n      .match-sub {\n        font-size: 11px;\n      }\n\n      .match-spotify iframe {\n        min-height: 152px;\n      }\n\n      .match-tip {\n        font-size: 13px;\n      }\n    }\n\n    @media (max-width: 400px) {\n      .demo-app {\n        padding-inline: 2px;\n      }\n\n      .top-logo {\n        width: min(214px, 62vw);\n      }\n\n      .attempts-card,\n      .button,\n      .secondary-button,\n      .streak-main {\n        font-size: 12px;\n      }\n\n      .streak-sub {\n        font-size: 8px;\n      }\n    }\n\n    @media (hover: none), (pointer: coarse) {\n      .tab:hover,\n      .ghost-button:hover,\n      .button:hover,\n      .secondary-button:hover,\n      .streak-card.is-guest:hover,\n      .suggestion:hover {\n        transform: none;\n      }\n\n      .button:hover,\n      .secondary-button:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .demo-app.is-ranked-mode .match-ranked-form .button:hover,\n      .demo-app.is-ranked-mode .match-ranked-form .secondary-button.is-primary:hover {\n        background: var(--ranked-accent);\n        border-color: var(--ranked-accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover {\n        background: var(--accent);\n        border-color: var(--accent);\n        color: #111;\n      }\n\n      .streak-card.is-guest:hover .streak-sub {\n        color: rgba(17, 17, 17, 0.74);\n      }\n\n      .suggestion:hover {\n        background: transparent;\n      }\n\n      .button:active,\n      .secondary-button:active,\n      .streak-card.is-guest:active,\n      .tab:active,\n      .ghost-button:active,\n      .suggestion:active {\n        transform: scale(0.99);\n      }\n    }\n\n    @media (max-width: 720px) {\n      .demo-app .top-logo {\n        margin-top: 14px !important;\n      }\n\n      .demo-app .controls-inner {\n        margin-bottom: 18px !important;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"demo-app\">\n    <div class=\"app-shell\">\n      <header class=\"topbar\">\n        <img decoding=\"async\" class=\"top-logo\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png\" alt=\"BulldogMatch\">\n      <\/header>\n\n      <section class=\"controls-shell\" aria-label=\"Controlli BulldogMatch\">\n        <div class=\"controls-inner\">\n          <button class=\"tab is-active\" data-mode=\"classic\" type=\"button\">Daily<\/button>\n          <button class=\"tab\" data-mode=\"practice\" type=\"button\">Allenamento<\/button>\n          <button id=\"rankedModeButton\" class=\"tab tab-ranked-mobile\" type=\"button\">Classificata<\/button>\n          <button id=\"helpToggle\" class=\"ghost-button\" type=\"button\">Come si gioca<\/button>\n        <\/div>\n        <div id=\"helpPanelMobileSlot\" class=\"help-panel-mobile-slot\"><\/div>\n      <\/section>\n\n      <main class=\"shell\">\n        <section class=\"game-shell\">\n          <section id=\"helpPanel\" class=\"help-panel\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Obiettivo<\/strong>\n                <span>Trova l'artista segreto leggendo il punteggio di affinit\u00e0 di ogni tentativo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Affinit\u00e0<\/strong>\n                <span>0-100%. Pi\u00f9 il numero sale, pi\u00f9 il guess condivide tratti con il target.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Cosa Conta davvero<\/strong>\n                <span>Crew, collab, stile, sottogenere, regione di nascita, artisti simili, debutto e tipologia.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Daily<\/strong>\n                <span>Hai 20 tentativi. La partita del giorno si salva in locale.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Allenamento<\/strong>\n                <span>Fai pratica, puoi cambiare artista quando vuoi con il pulsante NUOVO.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Lettura veloce<\/strong>\n                <span>\ud83d\udfe2 = molto vicino. <br>\ud83d\udfe1 = buona base. <br>\ud83d\udfe0 = tracce utili. <\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedHelpPanel\" class=\"help-panel hidden\" aria-hidden=\"true\">\n            <div class=\"help-grid\">\n              <div class=\"help-card\">\n                <strong>Timer<\/strong>\n                <span>Hai 5 minuti per fare il punteggio pi\u00f9 alto possibile.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Punti<\/strong>\n                <span>Ogni artista corretto vale 1 punto e aggiunge 30 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Skip<\/strong>\n                <span>Puoi saltare il round corrente, ma perdi 10 secondi.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Tentativi<\/strong>\n                <span>Hai 20 tentativi per artista. I guess restano ordinati per affinit\u00e0.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Reveal gratis<\/strong>\n                <span>L'artista corretto del round appena chiuso diventa l'indizio iniziale del round dopo.<\/span>\n              <\/div>\n              <div class=\"help-card\">\n                <strong>Record<\/strong>\n                <span>Quando il backend ranked \u00e8 presente, il miglior punteggio viene salvato come nell'originale.<\/span>\n              <\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"standardGamePanel\" class=\"match-standard-panel\">\n            <form id=\"guessForm\" class=\"input-grid\" autocomplete=\"off\">\n              <div id=\"attemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label id=\"inputLabel\" class=\"input-label\" for=\"artistInput\">inserisci un artista<\/label>\n                <input id=\"artistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"gameMsg\">\n                <div id=\"suggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti\"><\/div>\n              <\/div>\n\n              <button id=\"submitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"modeActionButton\" class=\"secondary-button is-primary hidden\" type=\"button\">NUOVO<\/button>\n                <a id=\"dailyStreakCard\" class=\"streak-card hidden is-guest\" href=\"https:\/\/bulldogmusik.it\/account-login\/\" aria-label=\"Accedi per salvare la streak\">\n                  <span class=\"streak-copy\">\n                    <span id=\"dailyStreakMain\" class=\"streak-main\">STREAK<\/span>\n                    <span id=\"dailyStreakSub\" class=\"streak-sub\">Accedi per salvarla<\/span>\n                  <\/span>\n                <\/a>\n              <\/div>\n            <\/form>\n\n            <div id=\"gameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"resultsList\" class=\"results-list\">\n              <div class=\"empty-state\">Nessun tentativo ancora.<\/div>\n            <\/div>\n          <\/section>\n\n          <section id=\"rankedGamePanel\" class=\"match-ranked-panel hidden\">\n            <section class=\"match-meta-grid\" aria-label=\"Statistiche Classificata\">\n              <article class=\"match-meta-card is-accent\">\n                <span class=\"match-meta-label\">Tempo<\/span>\n                <strong id=\"rankedTimerValue\" class=\"match-meta-value\">5:00<\/strong>\n              <\/article>\n              <article class=\"match-meta-card\">\n                <span class=\"match-meta-label\">Punti<\/span>\n                <strong id=\"rankedScoreValue\" class=\"match-meta-value\">0<\/strong>\n              <\/article>\n              <article class=\"match-meta-card\">\n                <span class=\"match-meta-label\">Round<\/span>\n                <strong id=\"rankedRoundValue\" class=\"match-meta-value\">Pronto<\/strong>\n              <\/article>\n            <\/section>\n\n            <form id=\"rankedGuessForm\" class=\"input-grid match-ranked-form\" autocomplete=\"off\">\n              <div id=\"rankedAttemptsCard\" class=\"attempts-card\" aria-live=\"polite\">20 TENTATIVI<\/div>\n\n              <div class=\"input-shell\">\n                <label class=\"input-label\" for=\"rankedArtistInput\">inserisci un artista<\/label>\n                <input id=\"rankedArtistInput\" class=\"search-field\" type=\"text\" placeholder=\"inserisci un artista\" aria-describedby=\"rankedGameMsg\">\n                <div id=\"rankedSuggestions\" class=\"suggestions\" role=\"listbox\" aria-label=\"Suggerimenti artisti classificata\"><\/div>\n              <\/div>\n\n              <button id=\"rankedSubmitButton\" class=\"button\" type=\"submit\">PROVA<\/button>\n\n              <div class=\"action-slot\">\n                <button id=\"rankedSideActionButton\" class=\"secondary-button is-primary\" type=\"button\">INIZIA<\/button>\n              <\/div>\n            <\/form>\n\n            <section id=\"rankedSummaryPanel\" class=\"ranked-summary-panel hidden\" aria-live=\"polite\">\n              <h3 id=\"rankedSummaryTitle\" class=\"ranked-summary-title\">Partita conclusa<\/h3>\n              <div id=\"rankedSummaryLines\" class=\"ranked-summary-lines\"><\/div>\n            <\/section>\n\n            <div id=\"rankedGameMsg\" class=\"message\" aria-live=\"polite\"><\/div>\n\n            <div id=\"rankedResultsList\" class=\"results-list\">\n              <div class=\"empty-state\">ACCEDI per giocare.<\/div>\n            <\/div>\n          <\/section>\n        <\/section>\n      <\/main>\n    <\/div>\n  <\/div>\n\n  <script>\n    (async function () {\n      const bootstrapScript = document.currentScript;\n\n      async function loadScriptSource(src) {\n        return new Promise((resolve) => {\n          const existing = Array.from(document.querySelectorAll(\"script[src]\")).find((node) => node.src === src);\n          if (existing && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            resolve(true);\n            return;\n          }\n\n          const script = document.createElement(\"script\");\n          script.src = src;\n          script.async = false;\n          script.onload = () => resolve(true);\n          script.onerror = () => resolve(false);\n          document.head.appendChild(script);\n        });\n      }\n\n      async function ensureArtistsLoaded() {\n        if (Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n          return window.bulldogleArtists;\n        }\n\n        const sources = [\n          \".\/artists.js\",\n          \"artists.js\",\n          \"\/bulldogle\/artists.js?v=31\"\n        ];\n\n        if (window.location && \/^https?:\/i.test(window.location.protocol) && window.location.origin) {\n          sources.push(window.location.origin + \"\/bulldogle\/artists.js?v=31\");\n        }\n\n        sources.push(\"https:\/\/bulldogmusik.it\/bulldogle\/artists.js?v=31\");\n\n        for (const source of sources) {\n          const loaded = await loadScriptSource(source);\n          if (loaded && Array.isArray(window.bulldogleArtists) && window.bulldogleArtists.length) {\n            return window.bulldogleArtists;\n          }\n        }\n\n        return [];\n      }\n\n      const artistsSource = await ensureArtistsLoaded();\n      const matchConfig = window.bulldogleMatchConfig || {};\n      const rankedConfig = window.bulldogleRankedConfig || {};\n      const guestLoginUrl = \"https:\/\/bulldogmusik.it\/account-login\/\";\n      const CLASSIC_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogMatch-New.png\";\n      const RANKED_LOGO_URL = \"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/bulldogmatch-Ranked-1.png\";\n      const STANDARD_ATTEMPTS = 20;\n      const RANKED_ATTEMPTS = 20;\n      const RANKED_INITIAL_TIME = 300;\n      const RANKED_CORRECT_BONUS = 30;\n      const RANKED_SKIP_PENALTY = 10;\n      const RANKED_ROUND_TRANSITION_MS = 1000;\n      const RANKED_GAME_KEY = \"match\";\n      const hasRankedBackend = Boolean(rankedConfig && rankedConfig.ajaxUrl && rankedConfig.nonce);\n      const spotifyThumbStorageKey = \"bulldogmatch-spotify-thumbs-v1\";\n\n      const regionCoords = {\n        \"Abruzzo\": { lat: 42.3, lon: 13.9 },\n        \"Basilicata\": { lat: 40.6, lon: 15.8 },\n        \"Calabria\": { lat: 38.9, lon: 16.6 },\n        \"Campania\": { lat: 40.8, lon: 14.3 },\n        \"Emilia-Romagna\": { lat: 44.5, lon: 11.3 },\n        \"Friuli-Venezia Giulia\": { lat: 46.1, lon: 13.1 },\n        \"Lazio\": { lat: 41.9, lon: 12.5 },\n        \"Liguria\": { lat: 44.4, lon: 8.9 },\n        \"Lombardia\": { lat: 45.5, lon: 9.2 },\n        \"Marche\": { lat: 43.6, lon: 13.5 },\n        \"Molise\": { lat: 41.6, lon: 14.6 },\n        \"Piemonte\": { lat: 45.1, lon: 7.7 },\n        \"Puglia\": { lat: 41.1, lon: 16.9 },\n        \"Sardegna\": { lat: 40.1, lon: 9.0 },\n        \"Sicilia\": { lat: 37.6, lon: 14.0 },\n        \"Toscana\": { lat: 43.8, lon: 11.2 },\n        \"Trentino-Alto Adige\": { lat: 46.5, lon: 11.3 },\n        \"Umbria\": { lat: 43.1, lon: 12.4 },\n        \"Valle d'Aosta\": { lat: 45.7, lon: 7.3 },\n        \"Veneto\": { lat: 45.4, lon: 12.3 },\n        \"Estero\": null\n      };\n\n      const spotifyThumbCache = new Map();\n      const spotifyThumbRequests = new Map();\n      let standardMessageTimeoutId = 0;\n      let rankedMessageTimeoutId = 0;\n      let dailyStreakLoading = false;\n      let dailyStreakLoaded = false;\n\n      try {\n        const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n        Object.keys(storedThumbs).forEach((spotifyId) => {\n          if (storedThumbs[spotifyId]) {\n            spotifyThumbCache.set(spotifyId, String(storedThumbs[spotifyId]));\n          }\n        });\n      } catch (_) {}\n\n      const artists = artistsSource.map((artist) => ({\n        id: Number(artist.id),\n        name: String(artist.name || \"\").trim(),\n        city: String(artist.city || \"\").trim(),\n        region: String(artist.region || \"\").trim(),\n        debutYear: Number(artist.debutYear || 0),\n        members: String(artist.members || \"\").trim(),\n        gender: String(artist.gender || \"\").trim(),\n        popularity: String(artist.popularity || \"\").trim().toLowerCase(),\n        spotifyId: String(artist.spotifyId || \"\").trim(),\n        crew: String(artist.crew || \"\").trim(),\n        style: String(artist.style || \"\").trim(),\n        collab: String(artist.collab || \"\").trim(),\n        subgenre: String(artist.subgenre || \"\").trim(),\n        collabmid: String(artist.collabmid || \"\").trim(),\n        similar: String(artist.similar || \"\").trim(),\n        birthYear: Number(artist.birthYear || 0),\n        tip: String(artist.tip || \"\").trim()\n      })).filter((artist) => artist.id && artist.name);\n\n      const demoRoots = Array.from(document.querySelectorAll(\".demo-app\"));\n      const appRoot = bootstrapScript &&\n        bootstrapScript.previousElementSibling &&\n        bootstrapScript.previousElementSibling.classList &&\n        bootstrapScript.previousElementSibling.classList.contains(\"demo-app\")\n          ? bootstrapScript.previousElementSibling\n          : demoRoots[demoRoots.length - 1];\n\n      if (!appRoot) return;\n\n      const elements = {\n        topLogo: appRoot.querySelector(\".top-logo\"),\n        tabs: Array.from(appRoot.querySelectorAll(\".tab[data-mode]\")),\n        rankedModeButton: appRoot.querySelector(\"#rankedModeButton\"),\n        helpToggle: appRoot.querySelector(\"#helpToggle\"),\n        helpPanelMobileSlot: appRoot.querySelector(\"#helpPanelMobileSlot\"),\n        helpPanel: appRoot.querySelector(\"#helpPanel\"),\n        rankedHelpPanel: appRoot.querySelector(\"#rankedHelpPanel\"),\n        standardGamePanel: appRoot.querySelector(\"#standardGamePanel\"),\n        rankedGamePanel: appRoot.querySelector(\"#rankedGamePanel\"),\n        attemptsCard: appRoot.querySelector(\"#attemptsCard\"),\n        guessForm: appRoot.querySelector(\"#guessForm\"),\n        inputLabel: appRoot.querySelector(\"#inputLabel\"),\n        input: appRoot.querySelector(\"#artistInput\"),\n        suggestions: appRoot.querySelector(\"#suggestions\"),\n        submitButton: appRoot.querySelector(\"#submitButton\"),\n        modeActionButton: appRoot.querySelector(\"#modeActionButton\"),\n        dailyStreakCard: appRoot.querySelector(\"#dailyStreakCard\"),\n        dailyStreakMain: appRoot.querySelector(\"#dailyStreakMain\"),\n        dailyStreakSub: appRoot.querySelector(\"#dailyStreakSub\"),\n        message: appRoot.querySelector(\"#gameMsg\"),\n        resultsList: appRoot.querySelector(\"#resultsList\"),\n        rankedTimerValue: appRoot.querySelector(\"#rankedTimerValue\"),\n        rankedScoreValue: appRoot.querySelector(\"#rankedScoreValue\"),\n        rankedRoundValue: appRoot.querySelector(\"#rankedRoundValue\"),\n        rankedAttemptsCard: appRoot.querySelector(\"#rankedAttemptsCard\"),\n        rankedGuessForm: appRoot.querySelector(\"#rankedGuessForm\"),\n        rankedInput: appRoot.querySelector(\"#rankedArtistInput\"),\n        rankedSuggestions: appRoot.querySelector(\"#rankedSuggestions\"),\n        rankedSubmitButton: appRoot.querySelector(\"#rankedSubmitButton\"),\n        rankedSideActionButton: appRoot.querySelector(\"#rankedSideActionButton\"),\n        rankedSummaryPanel: appRoot.querySelector(\"#rankedSummaryPanel\"),\n        rankedSummaryTitle: appRoot.querySelector(\"#rankedSummaryTitle\"),\n        rankedSummaryLines: appRoot.querySelector(\"#rankedSummaryLines\"),\n        rankedMessage: appRoot.querySelector(\"#rankedGameMsg\"),\n        rankedResultsList: appRoot.querySelector(\"#rankedResultsList\")\n      };\n\n      if (!elements.guessForm || !elements.input || !elements.submitButton || !elements.suggestions) {\n        return;\n      }\n\n      const desktopHelpPanelParent = elements.helpPanel ? elements.helpPanel.parentElement : null;\n      const desktopHelpPanelNextSibling = elements.helpPanel ? elements.helpPanel.nextElementSibling : null;\n      const desktopRankedHelpPanelParent = elements.rankedHelpPanel ? elements.rankedHelpPanel.parentElement : null;\n      const desktopRankedHelpPanelNextSibling = elements.rankedHelpPanel ? elements.rankedHelpPanel.nextElementSibling : null;\n\n      if (elements.suggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.suggestions);\n      }\n\n      if (elements.rankedSuggestions.parentElement !== document.body) {\n        document.body.appendChild(elements.rankedSuggestions);\n      }\n\n      const italyDateInfo = getItalyDateInfo();\n      const dailyStorageKey = \"bulldogle_affinity_\" + italyDateInfo.dayKey;\n      const rankedLocalBestKey = \"bulldogmatch_ranked_demo_best\";\n\n      const app = {\n        mode: \"classic\",\n        modes: {\n          classic: createStandardState(getDailyTarget()),\n          practice: createPracticeState()\n        },\n        ranked: createRankedState()\n      };\n\n      if (!artists.length) {\n        elements.message.textContent = \"artists.js non e stato caricato correttamente.\";\n        elements.message.classList.add(\"is-visible\", \"is-error\");\n        disableStandardInput(true);\n        return;\n      }\n\n      function createStandardState(targetArtist) {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: targetArtist\n        };\n      }\n\n      function createPracticeState() {\n        return {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: 1,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n      }\n\n      function createRankedState() {\n        return {\n          active: false,\n          preparing: false,\n          submitting: false,\n          complete: false,\n          betweenRounds: false,\n          sessionToken: \"\",\n          currentArtist: null,\n          score: 0,\n          timeLeft: RANKED_INITIAL_TIME,\n          roundAttempts: 0,\n          roundGuessedIds: new Set(),\n          usedTargetIds: new Set(),\n          freeHintArtist: null,\n          results: [],\n          timerId: 0,\n          roundTransitionId: 0,\n          summaryTitle: \"Partita conclusa\",\n          summaryLines: [],\n          isRecord: false,\n          localBest: readLocalRankedBest()\n        };\n      }\n\n      function readLocalRankedBest() {\n        try {\n          return Number(window.localStorage.getItem(rankedLocalBestKey) || 0);\n        } catch (_) {\n          return 0;\n        }\n      }\n\n      function writeLocalRankedBest(score) {\n        const best = Math.max(readLocalRankedBest(), Number(score || 0));\n        try {\n          window.localStorage.setItem(rankedLocalBestKey, String(best));\n        } catch (_) {}\n        return best;\n      }\n\n      function normalize(value) {\n        return String(value || \"\").trim().toLowerCase();\n      }\n\n      function getItalyDateInfo() {\n        const formatter = new Intl.DateTimeFormat(\"en-CA\", {\n          timeZone: \"Europe\/Rome\",\n          year: \"numeric\",\n          month: \"2-digit\",\n          day: \"2-digit\"\n        });\n\n        const parts = formatter.formatToParts(new Date());\n        const year = Number(parts.find((part) => part.type === \"year\").value);\n        const month = Number(parts.find((part) => part.type === \"month\").value);\n        const day = Number(parts.find((part) => part.type === \"day\").value);\n\n        return {\n          dayKey: year + \"-\" + String(month).padStart(2, \"0\") + \"-\" + String(day).padStart(2, \"0\"),\n          dayIndex: Math.floor(Date.UTC(year, month - 1, day) \/ 86400000)\n        };\n      }\n\n      function getDailyTarget() {\n        return artists[(italyDateInfo.dayIndex + 137) % artists.length] || artists[0];\n      }\n\n      function pickRandomArtist(excludedIds) {\n        const blocked = excludedIds || new Set();\n        let pool = artists.filter((artist) => !blocked.has(artist.id));\n        if (!pool.length) pool = artists.slice();\n        return pool[Math.floor(Math.random() * pool.length)];\n      }\n\n      function getCurrentStandardState() {\n        return app.modes[app.mode];\n      }\n\n      function findArtistByName(name) {\n        const normalized = normalize(name);\n        return artists.find((artist) => normalize(artist.name) === normalized);\n      }\n\n      function normalizeCommaSeparatedList(listString) {\n        if (!listString) return [];\n        return listString\n          .split(\",\")\n          .map((item) => item.trim().toLowerCase())\n          .filter(Boolean);\n      }\n\n      function isInList(artistName, listString) {\n        if (!listString || !artistName) return false;\n        return normalizeCommaSeparatedList(listString).includes(artistName.toLowerCase());\n      }\n\n      function sharesListValue(firstListString, secondListString) {\n        const firstList = normalizeCommaSeparatedList(firstListString);\n        const secondList = normalizeCommaSeparatedList(secondListString);\n        if (!firstList.length || !secondList.length) return false;\n\n        const secondSet = new Set(secondList);\n        return firstList.some((item) => secondSet.has(item));\n      }\n\n      function calculateAffinity(guess, target) {\n        if (guess.id === target.id) return 100;\n\n        let score = 0;\n        const W_CREW = 24;\n        const W_COLLAB = 21;\n        const W_STYLE = 16;\n        const W_SUBGENRE = 10;\n        const W_REGION = 3;\n        const W_COLLABMID = 15;\n        const W_SIMILAR = 19;\n        const W_DEBUT_YEAR = 3;\n        const W_MEMBERS = 3;\n        const W_BIRTH_YEAR = 0;\n\n        if (sharesListValue(guess.crew, target.crew)) score += W_CREW;\n        if (isInList(target.name, guess.collab) || isInList(guess.name, target.collab)) score += W_COLLAB;\n        if (guess.style && target.style && guess.style === target.style) score += W_STYLE;\n        if (guess.subgenre && target.subgenre && guess.subgenre === target.subgenre) score += W_SUBGENRE;\n\n        if (guess.city === target.city) {\n          score += W_REGION;\n        } else if (guess.region === target.region) {\n          score += W_REGION * 0.6;\n        } else {\n          const g = regionCoords[guess.region];\n          const t = regionCoords[target.region];\n          if (g && t) {\n            const latDiff = g.lat - t.lat;\n            const lonDiff = g.lon - t.lon;\n            const dist = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);\n            const maxDist = 12;\n            if (dist < maxDist) {\n              score += (W_REGION * 0.3) * (1 - dist \/ maxDist);\n            }\n          }\n        }\n\n        if (isInList(target.name, guess.collabmid) || isInList(guess.name, target.collabmid)) score += W_COLLABMID;\n\n        const yearDiff = Math.abs(Number(guess.debutYear || 0) - Number(target.debutYear || 0));\n        if (yearDiff === 0) score += W_DEBUT_YEAR;\n        else if (yearDiff <= 2) score += W_DEBUT_YEAR * 0.8;\n        else if (yearDiff <= 5) score += W_DEBUT_YEAR * 0.5;\n        else if (yearDiff <= 10) score += W_DEBUT_YEAR * 0.2;\n\n        if (guess.members === target.members) score += W_MEMBERS;\n        if (isInList(target.name, guess.similar) || isInList(guess.name, target.similar)) score += W_SIMILAR;\n\n        if (guess.birthYear && target.birthYear) {\n          const birthDiff = Math.abs(Number(guess.birthYear || 0) - Number(target.birthYear || 0));\n          if (birthDiff === 0) score += W_BIRTH_YEAR;\n          else if (birthDiff <= 2) score += W_BIRTH_YEAR * 0.8;\n          else if (birthDiff <= 5) score += W_BIRTH_YEAR * 0.5;\n          else if (birthDiff <= 10) score += W_BIRTH_YEAR * 0.2;\n        }\n\n        return Math.min(99, Math.floor(score));\n      }\n\n      function getAffinityTone(affinity) {\n        if (affinity >= 100) return \"perfect\";\n        if (affinity >= 80) return \"high\";\n        if (affinity >= 50) return \"medium\";\n        if (affinity >= 30) return \"warm\";\n        return \"low\";\n      }\n\n      function getAffinityColor(affinity, ranked) {\n        if (affinity >= 100) return \"#7dc56a\";\n        if (affinity >= 80) return \"#7dc56a\";\n        if (affinity >= 50) return \"#f0b955\";\n        if (affinity >= 30) return ranked ? \"#952800\" : \"#f16c1d\";\n        return \"#ff7e59\";\n      }\n\n      function getAffinityDescriptor(affinity) {\n        if (affinity >= 100) return \"Hai indovinato\";\n        if (affinity >= 80) return \"Molto vicino\";\n        if (affinity >= 50) return \"Buona affinit\u00e0\";\n        if (affinity >= 30) return \"Tracce interessanti\";\n        return \"Molto lontano\";\n      }\n\n      function getArtistInitials(name) {\n        const parts = String(name || \"\").trim().split(\/\\s+\/).filter(Boolean);\n        if (!parts.length) return \"BM\";\n        return parts.slice(0, 2).map((part) => part.charAt(0)).join(\"\");\n      }\n\n      function persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl) {\n        if (!spotifyId || !thumbnailUrl) return;\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          storedThumbs[spotifyId] = thumbnailUrl;\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      function clearSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return;\n        spotifyThumbCache.delete(spotifyId);\n        spotifyThumbRequests.delete(spotifyId);\n        try {\n          const storedThumbs = JSON.parse(window.localStorage.getItem(spotifyThumbStorageKey) || \"{}\");\n          delete storedThumbs[spotifyId];\n          window.localStorage.setItem(spotifyThumbStorageKey, JSON.stringify(storedThumbs));\n        } catch (_) {}\n      }\n\n      async function getSpotifyThumbnailUrl(spotifyId) {\n        if (!spotifyId) return \"\";\n        if (spotifyThumbCache.has(spotifyId)) return spotifyThumbCache.get(spotifyId);\n        if (spotifyThumbRequests.has(spotifyId)) return spotifyThumbRequests.get(spotifyId);\n\n        const request = (async function () {\n          const sources = [\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"https:\/\/open.spotify.com\/artist\/\" + spotifyId),\n            \"https:\/\/open.spotify.com\/oembed?url=\" + encodeURIComponent(\"spotify:artist:\" + spotifyId)\n          ];\n\n          for (const source of sources) {\n            try {\n              const response = await fetch(source, { credentials: \"omit\" });\n              if (!response.ok) continue;\n              const payload = await response.json();\n              const thumbnailUrl = payload && payload.thumbnail_url ? String(payload.thumbnail_url) : \"\";\n              if (thumbnailUrl) {\n                spotifyThumbCache.set(spotifyId, thumbnailUrl);\n                persistSpotifyThumbnailUrl(spotifyId, thumbnailUrl);\n                return thumbnailUrl;\n              }\n            } catch (_) {}\n          }\n\n          return \"\";\n        })().finally(function () {\n          spotifyThumbRequests.delete(spotifyId);\n        });\n\n        spotifyThumbRequests.set(spotifyId, request);\n        return request;\n      }\n\n      function buildArtistCover(artist) {\n        const cover = document.createElement(\"div\");\n        cover.className = \"match-cover\";\n\n        const fallback = document.createElement(\"span\");\n        fallback.className = \"match-cover-fallback\";\n        fallback.textContent = getArtistInitials(artist.name);\n        cover.appendChild(fallback);\n\n        if (artist.spotifyId) {\n          getSpotifyThumbnailUrl(artist.spotifyId).then((thumbnailUrl) => {\n            if (!thumbnailUrl) return;\n            const image = document.createElement(\"img\");\n            image.alt = artist.name;\n            image.loading = \"eager\";\n            image.decoding = \"async\";\n            image.referrerPolicy = \"no-referrer\";\n\n            function applyThumbnail() {\n              cover.replaceChildren(image);\n            }\n\n            function handleThumbnailError() {\n              clearSpotifyThumbnailUrl(artist.spotifyId);\n            }\n\n            image.addEventListener(\"error\", handleThumbnailError, { once: true });\n            image.addEventListener(\"load\", applyThumbnail, { once: true });\n            image.src = thumbnailUrl;\n\n            if (image.complete) {\n              if (image.naturalWidth > 0) {\n                applyThumbnail();\n              } else {\n                handleThumbnailError();\n              }\n            }\n          });\n        }\n\n        return cover;\n      }\n\n      function buildSpotifyEmbed(spotifyId) {\n        if (!spotifyId) return null;\n        const wrap = document.createElement(\"div\");\n        wrap.className = \"match-spotify\";\n        const iframe = document.createElement(\"iframe\");\n        iframe.src = \"https:\/\/open.spotify.com\/embed\/artist\/\" + spotifyId + \"?utm_source=generator&theme=0\";\n        iframe.allow = \"autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture\";\n        iframe.loading = \"lazy\";\n        wrap.appendChild(iframe);\n        return wrap;\n      }\n\n      function buildArtistTip(tip) {\n        const cleanTip = String(tip || \"\").trim();\n        if (!cleanTip) return null;\n        const copy = document.createElement(\"p\");\n        copy.className = \"match-tip\";\n        copy.textContent = cleanTip;\n        return copy;\n      }\n\n      function buildResultEntry(artist, targetArtist, meta) {\n        const affinity = calculateAffinity(artist, targetArtist);\n        return {\n          artist: artist,\n          affinity: affinity,\n          kind: meta.kind || \"guess\",\n          label: meta.label || \"Tentativo\",\n          subcopy: meta.subcopy || getAffinityDescriptor(affinity),\n          solved: artist.id === targetArtist.id,\n          spotifyId: meta.spotifyId || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.spotifyId : \"\"),\n          tip: meta.tip || ((artist.id === targetArtist.id || meta.kind === \"answer\") ? targetArtist.tip : \"\"),\n          ranked: Boolean(meta.ranked)\n        };\n      }\n\n      function buildMatchResultCard(result) {\n        const article = document.createElement(\"article\");\n        article.className = \"match-card\";\n        if (result.solved) article.classList.add(\"is-solved\");\n        if (result.kind === \"answer\") article.classList.add(\"is-answer\");\n        if (result.kind === \"auto\") article.classList.add(\"is-auto\");\n\n        const top = document.createElement(\"div\");\n        top.className = \"match-top\";\n\n        const cover = buildArtistCover(result.artist);\n        const copy = document.createElement(\"div\");\n        copy.className = \"match-copy\";\n\n        const badge = document.createElement(\"span\");\n        badge.className = \"match-badge\";\n        badge.textContent = result.label;\n\n        const name = document.createElement(\"h4\");\n        name.className = \"match-name\";\n        name.textContent = result.artist.name;\n\n        const sub = document.createElement(\"p\");\n        sub.className = \"match-sub\";\n        sub.textContent = result.subcopy || getAffinityDescriptor(result.affinity);\n\n        copy.appendChild(badge);\n        copy.appendChild(name);\n        copy.appendChild(sub);\n\n        const score = document.createElement(\"div\");\n        score.className = \"match-score\";\n\n        const scoreMain = document.createElement(\"span\");\n        scoreMain.className = \"match-score-main\";\n        scoreMain.textContent = result.affinity + \"%\";\n\n        const scoreSub = document.createElement(\"span\");\n        scoreSub.className = \"match-score-sub\";\n        scoreSub.textContent = \"Affinit\u00e0\";\n\n        score.appendChild(scoreMain);\n        score.appendChild(scoreSub);\n\n        top.appendChild(cover);\n        top.appendChild(copy);\n        top.appendChild(score);\n\n        const meter = document.createElement(\"div\");\n        meter.className = \"match-meter\";\n        const fill = document.createElement(\"div\");\n        fill.className = \"match-meter-fill\";\n        fill.style.width = result.affinity + \"%\";\n        fill.style.background = getAffinityColor(result.affinity, result.ranked);\n        meter.appendChild(fill);\n\n        article.appendChild(top);\n        article.appendChild(meter);\n\n        const spotifyEmbed = buildSpotifyEmbed(result.spotifyId);\n        if (spotifyEmbed) article.appendChild(spotifyEmbed);\n        if (result.tip) {\n          const tipNode = buildArtistTip(result.tip);\n          if (tipNode) article.appendChild(tipNode);\n        }\n\n        return article;\n      }\n\n      function disableStandardInput(disabled) {\n        elements.input.disabled = disabled;\n        elements.submitButton.disabled = disabled;\n      }\n\n      function disableRankedInput(disabled) {\n        elements.rankedInput.disabled = disabled;\n        elements.rankedSubmitButton.disabled =\n          disabled ||\n          !app.ranked.active ||\n          app.ranked.betweenRounds ||\n          app.ranked.preparing ||\n          app.ranked.submitting;\n      }\n\n      function getViewportMetrics() {\n        if (window.visualViewport) {\n          return {\n            width: window.visualViewport.width,\n            height: window.visualViewport.height,\n            offsetLeft: window.visualViewport.offsetLeft,\n            offsetTop: window.visualViewport.offsetTop\n          };\n        }\n\n        return {\n          width: window.innerWidth,\n          height: window.innerHeight,\n          offsetLeft: 0,\n          offsetTop: 0\n        };\n      }\n\n      function positionSuggestionsFor(inputElement, suggestionElement) {\n        const rect = inputElement.getBoundingClientRect();\n        const viewport = getViewportMetrics();\n        const viewportPadding = 10;\n        const width = Math.min(rect.width, viewport.width - viewportPadding * 2);\n        const left = Math.max(\n          viewport.offsetLeft + viewportPadding,\n          Math.min(rect.left, viewport.offsetLeft + viewport.width - width - viewportPadding)\n        );\n        const spaceBelow = viewport.offsetTop + viewport.height - rect.bottom - viewportPadding;\n        const maxHeight = Math.max(140, Math.min(280, spaceBelow - 8));\n\n        suggestionElement.style.width = width + \"px\";\n        suggestionElement.style.left = left + \"px\";\n        suggestionElement.style.top = Math.max(viewportPadding, rect.bottom + 8) + \"px\";\n        suggestionElement.style.maxHeight = maxHeight + \"px\";\n      }\n\n      function hideSuggestions() {\n        elements.suggestions.style.display = \"none\";\n      }\n\n      function showSuggestions() {\n        positionSuggestionsFor(elements.input, elements.suggestions);\n        elements.suggestions.style.display = \"block\";\n      }\n\n      function hideRankedSuggestions() {\n        elements.rankedSuggestions.style.display = \"none\";\n      }\n\n      function showRankedSuggestions() {\n        positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        elements.rankedSuggestions.style.display = \"block\";\n      }\n\n      function getCurrentHelpPanel() {\n        return app.mode === \"ranked\" ? elements.rankedHelpPanel : elements.helpPanel;\n      }\n\n      function closeAllHelpPanels() {\n        [elements.helpPanel, elements.rankedHelpPanel].forEach((panel) => {\n          if (!panel) return;\n          panel.classList.remove(\"is-open\");\n          panel.setAttribute(\"aria-hidden\", \"true\");\n        });\n        elements.helpToggle.classList.remove(\"is-active\");\n      }\n\n      function syncHelpToggleState() {\n        const panel = getCurrentHelpPanel();\n        elements.helpToggle.classList.toggle(\"is-active\", !!panel && panel.classList.contains(\"is-open\"));\n      }\n\n      function restorePanelPlacement(panel, parent, nextSibling) {\n        if (!panel || !parent) return;\n        if (panel.parentElement === parent) return;\n\n        if (nextSibling && nextSibling.parentElement === parent) {\n          parent.insertBefore(panel, nextSibling);\n          return;\n        }\n\n        parent.appendChild(panel);\n      }\n\n      function syncHelpPanelPlacement() {\n        if (!elements.helpPanelMobileSlot) {\n          return;\n        }\n\n        const isMobileLayout = window.matchMedia(\"(max-width: 720px)\").matches;\n        const activePanel = getCurrentHelpPanel();\n\n        if (isMobileLayout) {\n          [elements.helpPanel, elements.rankedHelpPanel].forEach((panel) => {\n            if (!panel) return;\n\n            if (panel === activePanel) {\n              if (panel.parentElement !== elements.helpPanelMobileSlot) {\n                elements.helpPanelMobileSlot.appendChild(panel);\n              }\n              return;\n            }\n\n            if (panel === elements.helpPanel) {\n              restorePanelPlacement(panel, desktopHelpPanelParent, desktopHelpPanelNextSibling);\n              return;\n            }\n\n            restorePanelPlacement(panel, desktopRankedHelpPanelParent, desktopRankedHelpPanelNextSibling);\n          });\n          return;\n        }\n\n        restorePanelPlacement(elements.helpPanel, desktopHelpPanelParent, desktopHelpPanelNextSibling);\n        restorePanelPlacement(elements.rankedHelpPanel, desktopRankedHelpPanelParent, desktopRankedHelpPanelNextSibling);\n      }\n\n      function syncHelpPanelVisibility() {\n        const rankedActive = app.mode === \"ranked\";\n        elements.helpPanel.classList.toggle(\"hidden\", rankedActive);\n        elements.rankedHelpPanel.classList.toggle(\"hidden\", !rankedActive);\n      }\n\n      function setMessage(text, tone) {\n        if (standardMessageTimeoutId) {\n          window.clearTimeout(standardMessageTimeoutId);\n          standardMessageTimeoutId = 0;\n        }\n\n        elements.message.className = \"message\";\n        if (text && tone) {\n          elements.message.textContent = text;\n          elements.message.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\" && tone !== \"error-final\") {\n            standardMessageTimeoutId = window.setTimeout(function () {\n              elements.message.className = \"message\";\n              elements.message.textContent = \"\";\n              standardMessageTimeoutId = 0;\n            }, 3000);\n          }\n          return;\n        }\n\n        elements.message.textContent = \"\";\n      }\n\n      function setRankedMessage(text, tone) {\n        if (rankedMessageTimeoutId) {\n          window.clearTimeout(rankedMessageTimeoutId);\n          rankedMessageTimeoutId = 0;\n        }\n\n        elements.rankedMessage.className = \"message\";\n        if (text && tone) {\n          elements.rankedMessage.textContent = text;\n          elements.rankedMessage.classList.add(\"is-visible\", \"is-\" + tone);\n\n          if (tone !== \"success\") {\n            rankedMessageTimeoutId = window.setTimeout(function () {\n              elements.rankedMessage.className = \"message\";\n              elements.rankedMessage.textContent = \"\";\n              rankedMessageTimeoutId = 0;\n            }, 2200);\n          }\n          return;\n        }\n\n        elements.rankedMessage.textContent = \"\";\n      }\n\n      function renderBranding() {\n        const rankedActive = app.mode === \"ranked\";\n        const nextSrc = rankedActive ? RANKED_LOGO_URL : CLASSIC_LOGO_URL;\n        const nextAlt = rankedActive ? \"BulldogMatch Classificato\" : \"BulldogMatch\";\n\n        if (elements.topLogo.getAttribute(\"src\") !== nextSrc) {\n          elements.topLogo.setAttribute(\"src\", nextSrc);\n        }\n\n        elements.topLogo.setAttribute(\"alt\", nextAlt);\n        appRoot.classList.toggle(\"is-ranked-mode\", rankedActive);\n        document.body.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n        document.documentElement.classList.toggle(\"bulldogmatch-ranked-mode-active\", rankedActive);\n      }\n\n      function renderTabs() {\n        elements.tabs.forEach((tab) => {\n          tab.classList.toggle(\"is-active\", tab.dataset.mode === app.mode);\n        });\n        elements.rankedModeButton.classList.toggle(\"is-active\", app.mode === \"ranked\");\n      }\n\n      function renderPanels() {\n        const rankedActive = app.mode === \"ranked\";\n        elements.standardGamePanel.classList.toggle(\"hidden\", rankedActive);\n        elements.rankedGamePanel.classList.toggle(\"hidden\", !rankedActive);\n        syncHelpPanelVisibility();\n        syncHelpPanelPlacement();\n      }\n\n      function renderStandardAttempts() {\n        const state = getCurrentStandardState();\n        const attemptsLeft = Math.max(0, state.maxAttempts - state.attempts);\n        elements.attemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderStandardActionSlot() {\n        if (app.mode === \"classic\") {\n          elements.modeActionButton.classList.add(\"hidden\");\n          elements.dailyStreakCard.classList.remove(\"hidden\");\n          loadDailyStreakStatus();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.add(\"hidden\");\n        elements.modeActionButton.className = \"secondary-button is-primary\";\n        elements.modeActionButton.classList.remove(\"hidden\");\n        elements.modeActionButton.textContent = \"NUOVO\";\n        elements.modeActionButton.disabled = false;\n      }\n\n      function renderStandardInputState() {\n        const state = getCurrentStandardState();\n        disableStandardInput(state.complete);\n        elements.inputLabel.textContent = \"inserisci un artista\";\n        if (app.mode === \"classic\" && state.complete) {\n          elements.input.placeholder = \"Torna domani!\";\n        } else if (app.mode === \"practice\" && state.complete) {\n          elements.input.placeholder = \"Premi NUOVO per ricominciare\";\n        } else {\n          elements.input.placeholder = \"inserisci un artista\";\n        }\n      }\n\n      function renderStandardResults() {\n        const state = getCurrentStandardState();\n        elements.resultsList.innerHTML = \"\";\n\n        if (!state.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = \"Nessun tentativo ancora.\";\n          elements.resultsList.appendChild(empty);\n          return;\n        }\n\n        state.results.forEach((result) => {\n          elements.resultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedMeta() {\n        elements.rankedTimerValue.textContent = formatTime(app.ranked.timeLeft);\n        elements.rankedScoreValue.textContent = String(app.ranked.score);\n        elements.rankedRoundValue.textContent = app.ranked.active\n          ? \"Artista #\" + String(app.ranked.score + 1)\n          : (app.ranked.complete ? \"Fine\" : \"Pronto\");\n      }\n\n      function renderRankedAttempts() {\n        const attemptsLeft = Math.max(0, RANKED_ATTEMPTS - app.ranked.roundAttempts);\n        elements.rankedAttemptsCard.textContent = attemptsLeft + \" \" + (attemptsLeft === 1 ? \"TENTATIVO\" : \"TENTATIVI\");\n      }\n\n      function renderRankedInputState() {\n        disableRankedInput(!app.ranked.active);\n        elements.rankedInput.placeholder = app.ranked.active ? \"inserisci un artista\" : (app.ranked.complete ? \"Partita conclusa\" : \"inserisci un artista\");\n      }\n\n      function renderRankedActionButton() {\n        const button = elements.rankedSideActionButton;\n        button.className = \"secondary-button is-primary\";\n        button.disabled = false;\n\n        if (app.ranked.preparing || app.ranked.submitting) {\n          button.textContent = \"...\";\n          button.disabled = true;\n          button.dataset.action = \"wait\";\n          return;\n        }\n\n        if (app.ranked.active) {\n          if (app.ranked.betweenRounds) {\n            button.textContent = \"ATTENDI\";\n            button.disabled = true;\n            button.dataset.action = \"wait\";\n            return;\n          }\n          button.textContent = \"SKIP\";\n          button.dataset.action = \"skip\";\n          return;\n        }\n\n        if (app.ranked.complete) {\n          button.textContent = \"RIGIOCA\";\n          button.dataset.action = \"restart\";\n          return;\n        }\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          button.textContent = \"ACCEDI\";\n          button.dataset.action = \"login\";\n          return;\n        }\n\n        button.textContent = \"INIZIA\";\n        button.dataset.action = \"start\";\n      }\n\n      function renderRankedResults() {\n        elements.rankedResultsList.innerHTML = \"\";\n\n        if (!app.ranked.results.length) {\n          const empty = document.createElement(\"div\");\n          empty.className = \"empty-state\";\n          empty.textContent = app.ranked.complete\n            ? \"La partita e finita. Puoi controllare il riepilogo finale.\"\n            : (app.ranked.active\n              ? \"I tuoi tentativi appariranno qui ordinati per affinit\u00e0.\"\n              : (hasRankedBackend && !rankedConfig.isLoggedIn ? \"ACCEDI per giocare.\" : \"Premi INIZIA per giocare.\"));\n          elements.rankedResultsList.appendChild(empty);\n          return;\n        }\n\n        app.ranked.results.forEach((result) => {\n          elements.rankedResultsList.appendChild(buildMatchResultCard(result));\n        });\n      }\n\n      function renderRankedSummary() {\n        elements.rankedSummaryPanel.classList.toggle(\"hidden\", !app.ranked.complete || !app.ranked.summaryLines.length);\n        elements.rankedSummaryPanel.classList.toggle(\"is-record\", app.ranked.isRecord);\n        elements.rankedSummaryTitle.textContent = app.ranked.summaryTitle || \"Partita conclusa\";\n        elements.rankedSummaryLines.innerHTML = \"\";\n\n        app.ranked.summaryLines.forEach((line) => {\n          const row = document.createElement(\"div\");\n          row.className = \"ranked-summary-line\";\n          row.innerHTML = line;\n          elements.rankedSummaryLines.appendChild(row);\n        });\n      }\n\n      function renderRankedPanel() {\n        renderRankedMeta();\n        renderRankedAttempts();\n        renderRankedInputState();\n        renderRankedActionButton();\n        renderRankedSummary();\n        renderRankedResults();\n        hideRankedSuggestions();\n      }\n\n      function renderApp() {\n        renderBranding();\n        renderTabs();\n        renderPanels();\n\n        if (app.mode === \"ranked\") {\n          renderRankedPanel();\n          hideSuggestions();\n          syncHelpToggleState();\n          return;\n        }\n\n        renderStandardAttempts();\n        renderStandardActionSlot();\n        renderStandardInputState();\n        renderStandardResults();\n        hideSuggestions();\n        hideRankedSuggestions();\n        syncHelpToggleState();\n      }\n\n      function formatTime(totalSeconds) {\n        const safeTime = Math.max(0, totalSeconds);\n        const minutes = Math.floor(safeTime \/ 60);\n        const seconds = String(safeTime % 60).padStart(2, \"0\");\n        return minutes + \":\" + seconds;\n      }\n\n      function getClassicSaveStatus() {\n        const state = app.modes.classic;\n        if (!state.complete) return \"playing\";\n        return state.results.some((result) => result.kind === \"guess\" && result.solved) ? \"won\" : \"lost\";\n      }\n\n      function saveClassicState(status) {\n        const state = app.modes.classic;\n        const payload = {\n          attempts: state.attempts,\n          guessedIds: Array.from(state.guessedIds),\n          guessedArtistIds: state.results.filter((result) => result.kind === \"guess\").map((result) => result.artist.id),\n          status: status || getClassicSaveStatus()\n        };\n\n        try {\n          window.localStorage.setItem(dailyStorageKey, JSON.stringify(payload));\n        } catch (_) {}\n      }\n\n      function loadClassicState() {\n        let rawState = \"\";\n\n        try {\n          rawState = window.localStorage.getItem(dailyStorageKey) || \"\";\n        } catch (_) {\n          return;\n        }\n\n        if (!rawState) return;\n\n        try {\n          const savedState = JSON.parse(rawState);\n          const state = app.modes.classic;\n          const savedArtistIds = Array.isArray(savedState.guessedArtistIds)\n            ? savedState.guessedArtistIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n            : [];\n          const restoredGuessedIds = new Set(\n            Array.isArray(savedState.guessedIds)\n              ? savedState.guessedIds.map((id) => Number(id)).filter((id) => Number.isFinite(id) && id > 0)\n              : []\n          );\n\n          state.results = [];\n          state.guessedIds = restoredGuessedIds;\n          state.attempts = Math.max(0, Math.min(state.maxAttempts, Number(savedState.attempts || savedArtistIds.length)));\n          state.complete = false;\n\n          savedArtistIds.forEach((id, index) => {\n            const artist = artists.find((entry) => entry.id === id);\n            if (!artist) return;\n\n            state.guessedIds.add(artist.id);\n            state.results.push(buildResultEntry(artist, state.target, {\n              kind: \"guess\",\n              label: \"Tentativo \" + (index + 1)\n            }));\n          });\n\n          state.results.sort((left, right) => right.affinity - left.affinity);\n\n          if (savedState.status === \"won\") {\n            state.complete = true;\n            setMessage(\"Hai indovinato \" + state.target.name + \".\", \"success\");\n            syncDailyStreakResult(\"won\");\n            return;\n          }\n\n          if (savedState.status === \"lost\") {\n            state.complete = true;\n            state.results.unshift(buildResultEntry(state.target, state.target, {\n              kind: \"answer\",\n              label: \"Artista corretto\"\n            }));\n            setMessage(\"Tentativi finiti. L'artista era \" + state.target.name + \".\", \"error-final\");\n            syncDailyStreakResult(\"lost\");\n            return;\n          }\n\n          setMessage(\"\", \"\");\n        } catch (error) {\n          console.error(\"Errore nel caricamento del Daily Match salvato:\", error);\n          try {\n            window.localStorage.removeItem(dailyStorageKey);\n          } catch (_) {}\n        }\n      }\n\n      function addStandardGuess(guessArtist, modeKey) {\n        const state = app.modes[modeKey];\n        const targetArtist = state.target;\n        const attemptNumber = state.attempts + 1;\n        const isSolved = guessArtist.id === targetArtist.id;\n\n        state.guessedIds.add(guessArtist.id);\n        state.attempts += 1;\n        state.results.push(buildResultEntry(guessArtist, targetArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + attemptNumber\n        }));\n        state.results.sort((left, right) => right.affinity - left.affinity);\n\n        if (isSolved) {\n          state.complete = true;\n          setMessage(\"Hai indovinato \" + targetArtist.name + \".\", \"success\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"won\");\n            syncDailyStreakResult(\"won\");\n          }\n          return;\n        }\n\n        if (state.attempts >= state.maxAttempts) {\n          state.complete = true;\n          state.results.unshift(buildResultEntry(targetArtist, targetArtist, {\n            kind: \"answer\",\n            label: \"Artista corretto\"\n          }));\n          setMessage(\"Tentativi finiti. L'artista era \" + targetArtist.name + \".\", \"error-final\");\n          if (modeKey === \"classic\") {\n            saveClassicState(\"lost\");\n            syncDailyStreakResult(\"lost\");\n          }\n          return;\n        }\n\n        setMessage(\"\", \"\");\n        if (modeKey === \"classic\") {\n          saveClassicState(\"playing\");\n        }\n      }\n\n      function submitStandardGuess(modeKey) {\n        const state = app.modes[modeKey];\n        if (state.complete) return;\n\n        const guessArtist = findArtistByName(elements.input.value.trim());\n        if (!guessArtist) {\n          setMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (state.guessedIds.has(guessArtist.id)) {\n          setMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.input.value = \"\";\n          return;\n        }\n\n        addStandardGuess(guessArtist, modeKey);\n        elements.input.value = \"\";\n      }\n\n      function startPracticeRound() {\n        const nextRound = (app.modes.practice.round || 0) + 1;\n        app.modes.practice = {\n          maxAttempts: STANDARD_ATTEMPTS,\n          attempts: 0,\n          round: nextRound,\n          guessedIds: new Set(),\n          results: [],\n          complete: false,\n          target: pickRandomArtist()\n        };\n        if (app.mode === \"practice\") {\n          setMessage(\"\", \"\");\n          renderApp();\n        }\n      }\n\n      function renderDailyStreakGuestState() {\n        elements.dailyStreakCard.classList.add(\"is-guest\");\n        elements.dailyStreakCard.setAttribute(\"href\", guestLoginUrl);\n        elements.dailyStreakCard.setAttribute(\"aria-label\", \"Accedi per salvare la streak\");\n        elements.dailyStreakMain.textContent = \"STREAK\";\n        elements.dailyStreakSub.textContent = \"Accedi per salvarla\";\n      }\n\n      function renderDailyStreakLoadingState() {\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        elements.dailyStreakMain.textContent = \"...\";\n        elements.dailyStreakSub.textContent = \"Caricamento streak\";\n      }\n\n      function renderDailyStreakState(data) {\n        if (!data || !data.is_logged_in) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        elements.dailyStreakCard.classList.remove(\"is-guest\");\n        elements.dailyStreakCard.removeAttribute(\"href\");\n        elements.dailyStreakCard.removeAttribute(\"aria-label\");\n        const streakValue = Number(data.current_streak || 0);\n        elements.dailyStreakMain.textContent = streakValue + \" \" + (streakValue === 1 ? \"giorno\" : \"giorni\");\n\n        if (data.today_result === \"won\") {\n          elements.dailyStreakSub.textContent = \"Indovinata oggi \u2022 Record \" + (data.best_streak || data.current_streak || 0);\n        } else if (data.today_result === \"lost\") {\n          elements.dailyStreakSub.textContent = \"Interrotta oggi \u2022 Record \" + (data.best_streak || 0);\n        } else {\n          elements.dailyStreakSub.textContent = \"Record \" + (data.best_streak || data.current_streak || 0);\n        }\n      }\n\n      async function postDailyStreakAction(action, extraData) {\n        const ajaxUrl = matchConfig.ajaxUrl;\n        if (!ajaxUrl) {\n          throw new Error(\"Config streak Match non trovata\");\n        }\n\n        const body = new URLSearchParams({\n          action: action,\n          nonce: matchConfig.nonce || \"\",\n          ...(extraData || {})\n        });\n\n        const response = await fetch(ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta streak Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      async function loadDailyStreakStatus(force) {\n        if (!matchConfig.isLoggedIn) {\n          renderDailyStreakGuestState();\n          return;\n        }\n\n        if (dailyStreakLoading) return;\n        if (dailyStreakLoaded && !force) return;\n\n        dailyStreakLoading = true;\n        renderDailyStreakLoadingState();\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_status\");\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n            dailyStreakLoaded = true;\n          } else {\n            renderDailyStreakGuestState();\n          }\n        } catch (_) {\n          renderDailyStreakGuestState();\n        } finally {\n          dailyStreakLoading = false;\n        }\n      }\n\n      async function syncDailyStreakResult(result) {\n        if (!matchConfig.isLoggedIn) return;\n\n        try {\n          const response = await postDailyStreakAction(\"bulldogle_match_streak_update\", {\n            result: result,\n            day_key: italyDateInfo.dayKey\n          });\n\n          if (response && response.success) {\n            renderDailyStreakState(response.data);\n          }\n        } catch (error) {\n          console.error(\"Errore aggiornamento streak Bulldog Match:\", error);\n        }\n      }\n\n      async function postRankedAction(action, extraData) {\n        const body = new URLSearchParams({\n          action: action,\n          nonce: rankedConfig.nonce || \"\",\n          game_key: RANKED_GAME_KEY,\n          ...(extraData || {})\n        });\n\n        const response = await fetch(rankedConfig.ajaxUrl, {\n          method: \"POST\",\n          credentials: \"same-origin\",\n          headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded; charset=UTF-8\"\n          },\n          body: body.toString()\n        });\n\n        if (!response.ok) {\n          throw new Error(\"Richiesta ranked Match non riuscita\");\n        }\n\n        return response.json();\n      }\n\n      function getRankedLoginUrl() {\n        return rankedConfig.loginUrl || guestLoginUrl;\n      }\n\n      function resetRankedState() {\n        const state = app.ranked;\n        state.active = false;\n        state.preparing = false;\n        state.submitting = false;\n        state.complete = false;\n        state.betweenRounds = false;\n        state.sessionToken = \"\";\n        state.currentArtist = null;\n        state.score = 0;\n        state.timeLeft = RANKED_INITIAL_TIME;\n        state.roundAttempts = 0;\n        state.roundGuessedIds = new Set();\n        state.usedTargetIds = new Set();\n        state.freeHintArtist = null;\n        state.results = [];\n        state.summaryTitle = \"Partita conclusa\";\n        state.summaryLines = [];\n        state.isRecord = false;\n        state.localBest = readLocalRankedBest();\n        window.clearInterval(state.timerId);\n        window.clearTimeout(state.roundTransitionId);\n        state.timerId = 0;\n        state.roundTransitionId = 0;\n        setRankedMessage(\"\", \"\");\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n      }\n\n      function startNextRankedArtist() {\n        if (!app.ranked.active) return;\n\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.betweenRounds = false;\n\n        if (app.ranked.usedTargetIds.size >= artists.length) {\n          app.ranked.usedTargetIds.clear();\n        }\n\n        const nextArtist = pickRandomArtist(app.ranked.usedTargetIds);\n        app.ranked.currentArtist = nextArtist;\n        app.ranked.usedTargetIds.add(nextArtist.id);\n        app.ranked.roundAttempts = 0;\n        app.ranked.roundGuessedIds = new Set();\n        app.ranked.results = [];\n\n        if (app.ranked.freeHintArtist) {\n          app.ranked.results.unshift(buildResultEntry(app.ranked.freeHintArtist, app.ranked.currentArtist, {\n            kind: \"auto\",\n            label: \"Reveal artista passato\",\n            subcopy: \"Indizio gratis dal round precedente\",\n            ranked: true\n          }));\n          app.ranked.freeHintArtist = null;\n        }\n\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedPanel();\n        if (app.mode === \"ranked\") {\n          elements.rankedInput.focus();\n        }\n      }\n\n      function completeRankedRound(message, tone, carryArtist) {\n        if (!app.ranked.active || !app.ranked.currentArtist) return;\n        if (carryArtist) app.ranked.freeHintArtist = app.ranked.currentArtist;\n\n        app.ranked.betweenRounds = true;\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n        renderRankedInputState();\n        renderRankedActionButton();\n        setRankedMessage(message, tone);\n\n        app.ranked.roundTransitionId = window.setTimeout(function () {\n          if (!app.ranked.active || app.ranked.timeLeft <= 0) return;\n          startNextRankedArtist();\n        }, RANKED_ROUND_TRANSITION_MS);\n      }\n\n      function tickRanked() {\n        if (!app.ranked.active) return;\n        app.ranked.timeLeft -= 1;\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n        }\n      }\n\n      async function startRankedGame() {\n        if (app.ranked.active || app.ranked.preparing) return;\n\n        if (hasRankedBackend && !rankedConfig.isLoggedIn) {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        app.ranked.preparing = true;\n        app.ranked.complete = false;\n        app.ranked.summaryLines = [];\n        renderRankedActionButton();\n\n        try {\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_start\");\n            if (!response || !response.success || !response.data || !response.data.is_logged_in) {\n              window.location.href = getRankedLoginUrl();\n              return;\n            }\n            app.ranked.sessionToken = String(response.data.token || \"\");\n          } else {\n            app.ranked.sessionToken = \"demo-\" + String(Date.now());\n          }\n\n          window.clearInterval(app.ranked.timerId);\n          window.clearTimeout(app.ranked.roundTransitionId);\n          app.ranked.active = true;\n          app.ranked.complete = false;\n          app.ranked.betweenRounds = false;\n          app.ranked.submitting = false;\n          app.ranked.score = 0;\n          app.ranked.timeLeft = RANKED_INITIAL_TIME;\n          app.ranked.roundAttempts = 0;\n          app.ranked.roundGuessedIds = new Set();\n          app.ranked.usedTargetIds = new Set();\n          app.ranked.freeHintArtist = null;\n          app.ranked.results = [];\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = [];\n          app.ranked.isRecord = false;\n          setRankedMessage(\"\", \"\");\n          renderRankedPanel();\n          startNextRankedArtist();\n          app.ranked.timerId = window.setInterval(tickRanked, 1000);\n        } catch (_) {\n          setRankedMessage(\"Non sono riuscito ad avviare la partita. Riprova tra poco.\", \"error\");\n        } finally {\n          app.ranked.preparing = false;\n          renderRankedInputState();\n          renderRankedActionButton();\n        }\n      }\n\n      function submitRankedGuess() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n\n        const guessArtist = findArtistByName(elements.rankedInput.value.trim());\n        if (!guessArtist) {\n          setRankedMessage(\"Artista non trovato nella lista.\", \"error\");\n          return;\n        }\n\n        if (app.ranked.roundGuessedIds.has(guessArtist.id)) {\n          setRankedMessage(\"Hai gia provato questo artista.\", \"error\");\n          elements.rankedInput.value = \"\";\n          return;\n        }\n\n        app.ranked.roundGuessedIds.add(guessArtist.id);\n        app.ranked.roundAttempts += 1;\n        app.ranked.results.push(buildResultEntry(guessArtist, app.ranked.currentArtist, {\n          kind: \"guess\",\n          label: \"Tentativo \" + app.ranked.roundAttempts,\n          ranked: true\n        }));\n        app.ranked.results.sort((left, right) => right.affinity - left.affinity);\n        renderRankedAttempts();\n        renderRankedResults();\n        elements.rankedInput.value = \"\";\n        hideRankedSuggestions();\n\n        if (guessArtist.id === app.ranked.currentArtist.id) {\n          app.ranked.score += 1;\n          app.ranked.timeLeft += RANKED_CORRECT_BONUS;\n          renderRankedMeta();\n          completeRankedRound(\"Hai indovinato!\", \"success\", true);\n          return;\n        }\n\n        if (app.ranked.roundAttempts >= RANKED_ATTEMPTS) {\n          completeRankedRound(\"Hai finito i tentativi! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n        }\n      }\n\n      function skipRankedArtist() {\n        if (!app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) return;\n        app.ranked.timeLeft = Math.max(0, app.ranked.timeLeft - RANKED_SKIP_PENALTY);\n        renderRankedMeta();\n        if (app.ranked.timeLeft <= 0) {\n          finishRankedGame();\n          return;\n        }\n        completeRankedRound(\"Hai skippato! Era \" + app.ranked.currentArtist.name + \".\", \"error\", true);\n      }\n\n      function escapeHtml(value) {\n        const div = document.createElement(\"div\");\n        div.textContent = value || \"\";\n        return div.innerHTML;\n      }\n\n      function buildRankedSummaryLines(data) {\n        const lines = [];\n        if (app.ranked.currentArtist && app.ranked.currentArtist.name) {\n          lines.push(\"L'ultimo artista era: <strong>\" + escapeHtml(app.ranked.currentArtist.name) + \"<\/strong>\");\n        }\n        lines.push(\"Punteggio finale: <strong>\" + String(app.ranked.score) + \"<\/strong>\");\n\n        if (data && typeof data.best_score !== \"undefined\") {\n          lines.push(\"Record mensile: <strong>\" + String(data.best_score) + \"<\/strong>\");\n          lines.push(\"Migliore posizione: <strong>\" + (data.best_rank ? \"#\" + data.best_rank : \"Fuori top 50\") + \"<\/strong>\");\n        } else {\n          const localBest = writeLocalRankedBest(app.ranked.score);\n          lines.push(\"Record demo: <strong>\" + String(localBest) + \"<\/strong>\");\n        }\n\n        return lines;\n      }\n\n      async function finishRankedGame() {\n        if (!app.ranked.active || app.ranked.submitting) return;\n\n        app.ranked.active = false;\n        app.ranked.complete = true;\n        app.ranked.betweenRounds = false;\n        app.ranked.submitting = true;\n        app.ranked.timeLeft = 0;\n        window.clearInterval(app.ranked.timerId);\n        window.clearTimeout(app.ranked.roundTransitionId);\n        app.ranked.timerId = 0;\n        app.ranked.roundTransitionId = 0;\n        hideRankedSuggestions();\n        setRankedMessage(\"\", \"\");\n        renderRankedMeta();\n        renderRankedInputState();\n        renderRankedActionButton();\n        app.ranked.summaryTitle = \"Partita conclusa\";\n        app.ranked.summaryLines = [\"Salvataggio record...\"];\n        app.ranked.isRecord = false;\n        renderRankedSummary();\n\n        try {\n          let data = null;\n          if (hasRankedBackend) {\n            const response = await postRankedAction(\"bulldogle_ranked_submit\", {\n              token: app.ranked.sessionToken,\n              score: String(app.ranked.score)\n            });\n            data = response && response.success ? response.data : null;\n          }\n\n          if (data) {\n            app.ranked.summaryTitle = data.is_record ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(data);\n            app.ranked.isRecord = Boolean(data.is_record);\n          } else {\n            const previousBest = readLocalRankedBest();\n            const nextBest = Math.max(previousBest, app.ranked.score);\n            writeLocalRankedBest(nextBest);\n            app.ranked.summaryTitle = nextBest === app.ranked.score && app.ranked.score > previousBest ? \"Nuovo record!\" : \"Partita conclusa\";\n            app.ranked.summaryLines = buildRankedSummaryLines(null);\n            app.ranked.isRecord = app.ranked.score > previousBest;\n          }\n        } catch (_) {\n          const previousBest = readLocalRankedBest();\n          const nextBest = Math.max(previousBest, app.ranked.score);\n          writeLocalRankedBest(nextBest);\n          app.ranked.summaryTitle = \"Partita conclusa\";\n          app.ranked.summaryLines = buildRankedSummaryLines(null);\n          app.ranked.isRecord = app.ranked.score > previousBest;\n        } finally {\n          app.ranked.submitting = false;\n          renderRankedPanel();\n        }\n      }\n\n      function buildSuggestionList(matches) {\n        elements.suggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.input.value = artist.name;\n            hideSuggestions();\n            elements.input.focus();\n          });\n          elements.suggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showSuggestions();\n          return;\n        }\n\n        hideSuggestions();\n      }\n\n      function updateSuggestions() {\n        const value = normalize(elements.input.value);\n        if (!value || getCurrentStandardState().complete) {\n          hideSuggestions();\n          return;\n        }\n\n        const guessedIds = getCurrentStandardState().guessedIds;\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !guessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        buildSuggestionList(matches);\n      }\n\n      function updateRankedSuggestions() {\n        const value = normalize(elements.rankedInput.value);\n        if (!value || !app.ranked.active || app.ranked.betweenRounds || !app.ranked.currentArtist) {\n          hideRankedSuggestions();\n          return;\n        }\n\n        const matches = artists.filter((artist) => {\n          return normalize(artist.name).includes(value) && !app.ranked.roundGuessedIds.has(artist.id);\n        }).slice(0, 8);\n\n        elements.rankedSuggestions.innerHTML = \"\";\n        matches.forEach((artist) => {\n          const button = document.createElement(\"button\");\n          button.type = \"button\";\n          button.className = \"suggestion\";\n\n          const left = document.createElement(\"span\");\n          left.textContent = artist.name;\n          const right = document.createElement(\"small\");\n          right.textContent = artist.city + \" \/ \" + artist.style;\n\n          button.appendChild(left);\n          button.appendChild(right);\n          button.addEventListener(\"click\", function () {\n            elements.rankedInput.value = artist.name;\n            hideRankedSuggestions();\n            elements.rankedInput.focus();\n          });\n          elements.rankedSuggestions.appendChild(button);\n        });\n\n        if (matches.length) {\n          showRankedSuggestions();\n          return;\n        }\n\n        hideRankedSuggestions();\n      }\n\n      function handleRankedSideAction() {\n        const action = elements.rankedSideActionButton.dataset.action;\n\n        if (action === \"login\") {\n          window.location.href = getRankedLoginUrl();\n          return;\n        }\n\n        if (action === \"start\" || action === \"restart\") {\n          startRankedGame();\n          return;\n        }\n\n        if (action === \"skip\") {\n          skipRankedArtist();\n        }\n      }\n\n      function switchMode(nextMode) {\n        if (nextMode === app.mode) return;\n\n        if (app.mode === \"ranked\" && nextMode !== \"ranked\") {\n          resetRankedState();\n        }\n\n        app.mode = nextMode;\n        elements.input.value = \"\";\n        elements.rankedInput.value = \"\";\n        hideSuggestions();\n        hideRankedSuggestions();\n        closeAllHelpPanels();\n        setMessage(\"\", \"\");\n        setRankedMessage(\"\", \"\");\n        renderApp();\n      }\n\n      elements.tabs.forEach((tab) => {\n        tab.addEventListener(\"click\", function () {\n          switchMode(tab.dataset.mode);\n        });\n      });\n\n      elements.rankedModeButton.addEventListener(\"click\", function () {\n        switchMode(\"ranked\");\n      });\n\n      elements.helpToggle.addEventListener(\"click\", function () {\n        const panel = getCurrentHelpPanel();\n        if (!panel) return;\n\n        const shouldOpen = !panel.classList.contains(\"is-open\");\n        closeAllHelpPanels();\n\n        if (shouldOpen) {\n          panel.classList.add(\"is-open\");\n          panel.setAttribute(\"aria-hidden\", \"false\");\n        }\n\n        syncHelpToggleState();\n      });\n\n      elements.dailyStreakCard.addEventListener(\"click\", function (event) {\n        if (elements.dailyStreakCard.classList.contains(\"is-guest\")) {\n          event.preventDefault();\n          window.location.href = elements.dailyStreakCard.getAttribute(\"href\") || guestLoginUrl;\n          return;\n        }\n\n        event.preventDefault();\n        event.stopPropagation();\n      });\n\n      elements.modeActionButton.addEventListener(\"click\", function () {\n        if (app.mode === \"practice\") {\n          startPracticeRound();\n        }\n      });\n\n      elements.guessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitStandardGuess(app.mode === \"classic\" ? \"classic\" : \"practice\");\n        renderApp();\n      });\n\n      elements.rankedGuessForm.addEventListener(\"submit\", function (event) {\n        event.preventDefault();\n        submitRankedGuess();\n        renderRankedPanel();\n      });\n\n      elements.rankedSideActionButton.addEventListener(\"click\", function () {\n        handleRankedSideAction();\n      });\n\n      elements.input.addEventListener(\"input\", updateSuggestions);\n      elements.input.addEventListener(\"focus\", updateSuggestions);\n      elements.rankedInput.addEventListener(\"input\", updateRankedSuggestions);\n      elements.rankedInput.addEventListener(\"focus\", updateRankedSuggestions);\n\n      document.addEventListener(\"click\", function (event) {\n        if (!elements.suggestions.contains(event.target) && event.target !== elements.input) {\n          hideSuggestions();\n        }\n\n        if (!elements.rankedSuggestions.contains(event.target) && event.target !== elements.rankedInput) {\n          hideRankedSuggestions();\n        }\n      });\n\n      window.addEventListener(\"resize\", function () {\n        syncHelpPanelPlacement();\n\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      });\n\n      window.addEventListener(\"scroll\", function () {\n        if (elements.suggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.input, elements.suggestions);\n        }\n\n        if (elements.rankedSuggestions.style.display === \"block\") {\n          positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n        }\n      }, true);\n\n      if (window.visualViewport) {\n        window.visualViewport.addEventListener(\"resize\", function () {\n          syncHelpPanelPlacement();\n\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n\n        window.visualViewport.addEventListener(\"scroll\", function () {\n          if (elements.suggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.input, elements.suggestions);\n          }\n\n          if (elements.rankedSuggestions.style.display === \"block\") {\n            positionSuggestionsFor(elements.rankedInput, elements.rankedSuggestions);\n          }\n        });\n      }\n\n      loadClassicState();\n      renderApp();\n      if (!app.modes.classic.complete) {\n        setMessage(\"\", \"\");\n      }\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6d91bf3 elementor-widget-divider--view-line_text elementor-widget-mobile__width-initial hide-when-ranked elementor-widget-divider--element-align-center elementor-widget elementor-widget-divider\" data-id=\"6d91bf3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"divider.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-divider\">\n\t\t\t<span class=\"elementor-divider-separator\">\n\t\t\t\t\t\t\t<span class=\"elementor-divider__text elementor-divider__element\">\n\t\t\t\tAltri Giochi\t\t\t\t<\/span>\n\t\t\t\t\t\t<\/span>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e109e40 hide-when-ranked elementor-widget__width-initial elementor-widget elementor-widget-post-widget\" data-id=\"e109e40\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;posts_per_page&quot;:100,&quot;post_type&quot;:[&quot;page&quot;],&quot;nb_columns&quot;:&quot;1&quot;,&quot;classic_layout&quot;:&quot;grid&quot;,&quot;nb_columns_tablet&quot;:&quot;3&quot;,&quot;nb_columns_mobile&quot;:&quot;1&quot;,&quot;post_status&quot;:&quot;publish&quot;,&quot;pagination&quot;:&quot;none&quot;}\" data-widget_type=\"post-widget.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"post-container none classic\" data-total-post=\"2\" ><div class=\"post-container-inner\"><div class=\"elementor-grid\" ><article class=\"post-wrapper\"  data-post-id=\"7057\" ><div class=\"post-image\"><a href=\"https:\/\/bulldogmusik.it\/en\/bulldogle-rap-ita\/\" ><img loading=\"lazy\" decoding=\"async\" width=\"1366\" height=\"768\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Bulldogle-Classico-New.png\" class=\"bpfwe-post-thumbnail\" alt=\"\" data-bpfwe-src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/Bulldogle-Classico-New.png\" loading=\"lazy\" \/><\/a><\/div><div class=\"inner-content\"><div class=\"post-content elementor-repeater-item-0b20400\"><p>Indovina il rapper in meno di 10 tentativi, ogni tentativo scopri nuove informazioni...<\/p><\/div><\/div><\/article><article class=\"post-wrapper\"  data-post-id=\"8846\" ><div class=\"post-image\"><a href=\"https:\/\/bulldogmusik.it\/en\/bulldogfeat-ita\/\" ><img loading=\"lazy\" decoding=\"async\" width=\"1366\" height=\"768\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogFeat-Logo-1.png\" class=\"bpfwe-post-thumbnail\" alt=\"\" data-bpfwe-src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/BulldogFeat-Logo-1.png\" loading=\"lazy\" \/><\/a><\/div><div class=\"inner-content\"><div class=\"post-content elementor-repeater-item-0b20400\"><p>Indovina in rapper in meno di 5 tentativi, ogni tentativo scopri altre collaborazioni....<\/p><\/div><\/div><\/article><\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e050415 elementor-widget-divider--view-line_text elementor-widget-mobile__width-initial show-when-ranked elementor-widget-divider--element-align-center elementor-widget elementor-widget-divider\" data-id=\"e050415\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"divider.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-divider\">\n\t\t\t<span class=\"elementor-divider-separator\">\n\t\t\t\t\t\t\t<span class=\"elementor-divider__text elementor-divider__element\">\n\t\t\t\tRanked\t\t\t\t<\/span>\n\t\t\t\t\t\t<\/span>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a79a216 elementor-widget-mobile__width-initial show-when-ranked elementor-widget elementor-widget-html\" data-id=\"a79a216\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"ranked-board-widget ranked-sidebar-board\" data-game-key=\"match\">\n  <div class=\"ranked-sidebar-board-inner\">\n    <div class=\"ranked-board-controls\" aria-label=\"Seleziona mese classifica\">\n      <select class=\"ranked-board-period-select\">\n        <option value=\"\">Caricamento...<\/option>\n      <\/select>\n    <\/div>\n\n    <img decoding=\"async\" class=\"ranked-sidebar-board-heading-image\" src=\"https:\/\/bulldogmusik.it\/wp-content\/uploads\/2026\/05\/LeadBasico.png\" alt=\"Classifica\">\n\n    <div class=\"ranked-board-list\"><\/div>\n\n    <div class=\"ranked-board-pager\">\n      <button class=\"ranked-board-prev\" type=\"button\" aria-label=\"Pagina precedente\">&#8249;<\/button>\n      <span class=\"ranked-board-page\">1\/1<\/span>\n      <button class=\"ranked-board-next\" type=\"button\" aria-label=\"Pagina successiva\">&#8250;<\/button>\n    <\/div>\n\n    <div class=\"ranked-board-msg\">Caricamento classifica...<\/div>\n  <\/div>\n<\/div>\n\n<script src=\"\/bulldogle\/ranked-leaderboard.js?v=58\"><\/script>\n\n<style>\n.ranked-sidebar-board {\n  --ranked-accent: #952800;\n  --ranked-accent-2: #a83100;\n  --ranked-accent-3: #7d2100;\n  --ranked-ink: #17110d;\n  --ranked-ink-soft: rgba(23, 17, 13, 0.72);\n  --ranked-cream: #f8f0e8;\n  width: 100%;\n  max-width: 100%;\n  margin: 0;\n  font-family: Verdana, Geneva, sans-serif;\n}\n\n.ranked-sidebar-board-inner {\n  position: relative;\n  width: 100%;\n  padding: 22px 22px 20px;\n  border-radius: 34px;\n  overflow: hidden;\n  background: var(--ranked-accent);\n  box-shadow:\n    inset 0 1px 0 rgba(255, 255, 255, 0.16),\n    0 18px 34px rgba(0, 0, 0, 0.18);\n  color: var(--ranked-ink);\n  text-align: center;\n}\n\n.ranked-sidebar-board-heading-image {\n  display: block;\n  width: min(100%, 206px) !important;\n  height: auto;\n  margin: 2px auto 16px;\n}\n.ranked-sidebar-board .ranked-board-controls {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  margin-bottom: 12px;\n}\n\n.ranked-sidebar-board .ranked-board-controls select {\n  width: 100% !important;\n  max-width: 182px;\n  min-width: 0;\n  height: 40px;\n  padding: 0 14px !important;\n  border: 1px solid rgba(23, 17, 13, 0.12) !important;\n  border-radius: 999px !important;\n  background: rgba(12, 8, 6, 0.9) !important;\n  color: var(--ranked-cream) !important;\n  font-family: Verdana, Geneva, sans-serif;\n  font-size: 12px !important;\n  font-weight: 700;\n  text-align: center;\n  box-shadow: none !important;\n  outline: none;\n  appearance: none;\n  -webkit-appearance: none;\n}\n\n.ranked-sidebar-board .ranked-board-list {\n  display: grid;\n  gap: 10px;\n  margin: 0;\n}\n\n.ranked-sidebar-board .ranked-board-row {\n  display: grid;\n  grid-template-columns: 66px minmax(0, 1fr) 64px;\n  align-items: center;\n  gap: 8px;\n  min-height: 52px;\n  padding: 10px 14px;\n  border: 0;\n  border-radius: 18px;\n  background: #000000;\n  color: var(--ranked-cream);\n  text-align: center;\n}\n\n.ranked-sidebar-board .ranked-board-row:first-child {\n  background: #000000;\n}\n\n.ranked-sidebar-board .ranked-board-rank {\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-name {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-score {\n  color: #ffffff;\n  font-size: 13px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-pager {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  gap: 12px;\n  margin-top: 14px;\n}\n\n.ranked-sidebar-board .ranked-board-pager button {\n  appearance: none !important;\n  display: inline-flex !important;\n  align-items: center !important;\n  justify-content: center !important;\n  width: 36px !important;\n  min-width: 36px !important;\n  height: 36px !important;\n  min-height: 36px !important;\n  padding: 0 !important;\n  margin: 0 !important;\n  border: 1px solid rgba(23, 17, 13, 0.12) !important;\n  border-radius: 999px !important;\n  background: #000000 !important;\n  color: #ffffff !important;\n  font-family: Verdana, Geneva, sans-serif !important;\n  font-size: 23px !important;\n  font-weight: 700 !important;\n  line-height: 1 !important;\n  box-shadow: none !important;\n  transform: none !important;\n  cursor: pointer;\n}\n\n.ranked-sidebar-board .ranked-board-pager button:hover {\n  background: #ffffff !important;\n  color: var(--ranked-ink) !important;\n}\n\n.ranked-sidebar-board .ranked-board-pager button:disabled {\n  opacity: 0.34;\n  cursor: not-allowed;\n}\n\n.ranked-sidebar-board .ranked-board-page {\n  color: rgba(23, 17, 13, 0.72);\n  font-size: 12px;\n  font-weight: 700;\n}\n\n.ranked-sidebar-board .ranked-board-msg {\n  min-height: 18px;\n  margin-top: 12px;\n  color: rgba(23, 17, 13, 0.72);\n  font-size: 12px;\n  line-height: 1.35;\n  font-weight: 700;\n  text-align: center;\n}\n\n@media (max-width: 767px) {\n  .ranked-sidebar-board-inner {\n    padding: 20px 18px 18px;\n    border-radius: 28px;\n  }\n\n  .ranked-sidebar-board .ranked-board-row {\n    grid-template-columns: 58px minmax(0, 1fr) 58px;\n    min-height: 48px;\n    padding: 9px 12px;\n  }\n\n  .ranked-sidebar-board-heading-image {\n    width: min(100%, 238px);\n  }\n}\n<\/style>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-9b1543b e-flex e-con-boxed wcf-starter-animations-none e-con e-parent\" data-id=\"9b1543b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-45b812a elementor-widget elementor-widget-html\" data-id=\"45b812a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style id=\"bulldogle-modern-font-900-override\">\n.demo-app button,\n.demo-app button *,\n.demo-app .attempts-card,\n.demo-app .attempts-card *,\n.demo-app .classic-attempts-badge,\n.demo-app .classic-attempts-badge *,\n.demo-app .classic-streak-badge,\n.demo-app .classic-streak-badge *,\n.demo-app .streak-card,\n.demo-app .streak-card *,\n.ranked-demo-app button,\n.ranked-demo-app button *,\n.ranked-demo-app .attempts-card,\n.ranked-demo-app .attempts-card *,\n.ranked-demo-app .classic-attempts-badge,\n.ranked-demo-app .classic-attempts-badge *,\n.ranked-demo-app .classic-streak-badge,\n.ranked-demo-app .classic-streak-badge *,\n.ranked-demo-app .streak-card,\n.ranked-demo-app .streak-card *,\n.demo-app .guess-name,\n.demo-app .guess-name *,\n.demo-app .artist-name,\n.demo-app .artist-name *,\n.demo-app .result-name,\n.demo-app .result-name *,\n.demo-app .board-row .name,\n.demo-app .board-row .name *,\n.ranked-demo-app .guess-name,\n.ranked-demo-app .guess-name *,\n.ranked-demo-app .artist-name,\n.ranked-demo-app .artist-name *,\n.ranked-demo-app .result-name,\n.ranked-demo-app .result-name *,\n.ranked-demo-app .board-row .name,\n.ranked-demo-app .board-row .name * {\n  font-family: Verdana, Geneva, sans-serif !important;\n  font-weight: 900 !important;\n  -webkit-text-stroke: 0.18px currentColor;\n  text-shadow: 0.18px 0 currentColor;\n}\n\n.demo-app .controls-inner .tab,\n.demo-app .controls-inner .ghost-button {\n  min-height: 56px !important;\n  height: 56px !important;\n  padding: 0 22px !important;\n  border-radius: 12px !important;\n  font-size: 15px !important;\n  line-height: 1.1 !important;\n  text-transform: uppercase !important;\n}\n\n.ranked-demo-app .meta-card,\n.ranked-demo-app .ranked-meta-button {\n  min-height: 74px !important;\n  height: 74px !important;\n  padding: 12px 14px !important;\n  border-radius: 16px !important;\n  font-size: 15px !important;\n  line-height: 1.1 !important;\n  text-transform: uppercase !important;\n}\n\n.ranked-demo-app .meta-label,\n.ranked-demo-app .meta-value {\n  line-height: 1.1 !important;\n}\n\n.demo-app.is-ranked-mode .match-meta-grid > .match-meta-card,\n.demo-app.is-ranked-mode .match-meta-grid > .ranked-meta-button,\nbody.bulldogmatch-ranked-mode-active .demo-app .match-meta-grid > .match-meta-card,\nbody.bulldogmatch-ranked-mode-active .demo-app .match-meta-grid > .ranked-meta-button,\n.ranked-demo-app .match-meta-grid > .match-meta-card,\n.ranked-demo-app .match-meta-grid > .ranked-meta-button {\n  min-height: 74px !important;\n  height: 74px !important;\n  align-self: stretch !important;\n}\n\n.ranked-demo-app .match-meta-label,\n.ranked-demo-app .match-meta-value {\n  line-height: 1.1 !important;\n}\n\n@media (max-width: 720px) {\n  .demo-app .top-logo,\n  .ranked-demo-app .top-logo {\n    margin-top: 14px !important;\n  }\n\n  .demo-app .controls-inner,\n  .ranked-demo-app .controls-inner {\n    margin-bottom: 18px !important;\n  }\n}\n\n<\/style>\n\t\t\t\t<\/div>\n\t\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>Indovina in rapper in meno di 20 tentativi, ogni tentativo scopri diverse affinit\u00e0. Giornaliera Classificata BulldogMatch Demo Daily Allenamento Classificata Come si gioca Obiettivo Trova l&#8217;artista segreto leggendo il punteggio di affinit\u00e0 di ogni tentativo. Affinit\u00e0 0-100%. Pi\u00f9 il numero sale, pi\u00f9 il guess condivide tratti con il target. Cosa Conta davvero Crew, collab, stile, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":14974,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_eb_attr":"","footnotes":""},"categories":[35],"tags":[],"class_list":["post-8209","page","type-page","status-publish","has-post-thumbnail","hentry","category-min"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":8}},"_links":{"self":[{"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/pages\/8209","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/comments?post=8209"}],"version-history":[{"count":20,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/pages\/8209\/revisions"}],"predecessor-version":[{"id":15545,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/pages\/8209\/revisions\/15545"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/media\/14974"}],"wp:attachment":[{"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/media?parent=8209"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/categories?post=8209"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bulldogmusik.it\/en\/wp-json\/wp\/v2\/tags?post=8209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}