var/cache/dev/twig/1f/1ff5085264f09f2f6d9ddf4ff43443fd13dd49b11c377b3ed22fa2480cbbdff6.php line 48

Open in your IDE?
  1. <?php
  2. use Twig\Environment;
  3. use Twig\Error\LoaderError;
  4. use Twig\Error\RuntimeError;
  5. use Twig\Extension\SandboxExtension;
  6. use Twig\Markup;
  7. use Twig\Sandbox\SecurityError;
  8. use Twig\Sandbox\SecurityNotAllowedTagError;
  9. use Twig\Sandbox\SecurityNotAllowedFilterError;
  10. use Twig\Sandbox\SecurityNotAllowedFunctionError;
  11. use Twig\Source;
  12. use Twig\Template;
  13. /* @NZEstimateSystem42/default/simulator.twig */
  14. class __TwigTemplate_ffdd72869d9b4d52aecce8a5eca7d16f67393c231120a1c96baa867d6de84b47 extends \Eccube\Twig\Template
  15. {
  16.     private $source;
  17.     private $macros = [];
  18.     public function __construct(Environment $env)
  19.     {
  20.         parent::__construct($env);
  21.         $this->source $this->getSourceContext();
  22.         $this->blocks = [
  23.             'main' => [$this'block_main'],
  24.         ];
  25.     }
  26.     protected function doGetParent(array $context)
  27.     {
  28.         // line 1
  29.         return "default_frame.twig";
  30.     }
  31.     protected function doDisplay(array $context, array $blocks = [])
  32.     {
  33.         $macros $this->macros;
  34.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  35.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->enter($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template""@NZEstimateSystem42/default/simulator.twig"));
  36.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02 $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  37.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->enter($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "template""@NZEstimateSystem42/default/simulator.twig"));
  38.         $this->parent $this->loadTemplate("default_frame.twig""@NZEstimateSystem42/default/simulator.twig"1);
  39.         $this->parent->display($contextarray_merge($this->blocks$blocks));
  40.         
  41.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->leave($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof);
  42.         
  43.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->leave($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof);
  44.     }
  45.     // line 3
  46.     public function block_main($context, array $blocks = [])
  47.     {
  48.         $macros $this->macros;
  49.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e $this->extensions["Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension"];
  50.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->enter($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""main"));
  51.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02 $this->extensions["Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension"];
  52.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->enter($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof = new \Twig\Profiler\Profile($this->getTemplateName(), "block""main"));
  53.         // line 4
  54.         echo "<style>
  55. /* フォント強制読み込み */
  56. @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap');
  57. * {
  58.     font-family: -apple-system, BlinkMacSystemFont, \"Helvetica Neue\", \"Segoe UI\", Arial, \"Hiragino Kaku Gothic ProN\", \"Hiragino Sans\", Meiryo, \"Noto Sans JP\", sans-serif !important;
  59. }
  60. /* 見積もりシミュレーター専用スタイル */
  61. .nz-estimate-wrapper {
  62.     max-width: 1200px;
  63.     margin: 60px auto;
  64.     padding: 0 20px;
  65. }
  66. .nz-estimate-card {
  67.     background: #ffffff;
  68.     border-radius: 20px;
  69.     box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
  70.     overflow: hidden;
  71. }
  72. .nz-estimate-header {
  73.     background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  74.     padding: 60px 40px;
  75.     text-align: center;
  76. }
  77. .nz-estimate-title {
  78.     font-size: 42px;
  79.     font-weight: 800;
  80.     color: #ffffff;
  81.     margin: 0 0 16px 0;
  82.     letter-spacing: -0.5px;
  83. }
  84. .nz-estimate-description {
  85.     font-size: 18px;
  86.     color: rgba(255, 255, 255, 0.95);
  87.     line-height: 1.7;
  88. }
  89. .nz-estimate-description img {
  90.     max-width: 100%;
  91.     height: auto;
  92.     display: block;
  93.     margin: 16px auto;
  94.     border-radius: 8px;
  95. }
  96. .nz-estimate-description p {
  97.     margin: 8px 0;
  98. }
  99. .nz-estimate-description br {
  100.     display: block;
  101.     content: \"\";
  102.     margin: 4px 0;
  103. }
  104. .nz-estimate-body {
  105.     padding: 50px 40px;
  106. }
  107. /* カテゴリセクション */
  108. .category-section {
  109.     margin-bottom: 20px;
  110.     overflow: hidden;
  111. }
  112. .category-row {
  113.     display: flex;
  114.     align-items: center;
  115.     gap: 20px;
  116.     width: 100%;
  117.     max-width: 100%;
  118. }
  119. .category-section.child-category {
  120.     margin-left: 40px;
  121.     padding-left: 20px;
  122.     border-left: 3px solid #cbd5e0;
  123.     display: none;
  124. }
  125. .category-section.child-category.active {
  126.     display: block;
  127.     animation: slideDown 0.3s ease;
  128. }
  129. @keyframes slideDown {
  130.     from { opacity: 0; transform: translateY(-10px); }
  131.     to { opacity: 1; transform: translateY(0); }
  132. }
  133. .category-header {
  134.     display: flex;
  135.     align-items: center;
  136.     gap: 15px;
  137.     margin-bottom: 0;
  138. }
  139. .category-title {
  140.     font-size: 20px;
  141.     font-weight: 700;
  142.     color: #2d3748;
  143.     margin: 0;
  144.     min-width: 150px;
  145.     flex-shrink: 0;
  146. }
  147. .category-title.child {
  148.     font-size: 18px;
  149.     color: #4a5568;
  150. }
  151. .category-required-badge {
  152.     display: inline-block;
  153.     background: #e53e3e;
  154.     color: white;
  155.     font-size: 12px;
  156.     font-weight: 700;
  157.     padding: 4px 12px;
  158.     border-radius: 12px;
  159. }
  160. .category-optional-badge {
  161.     display: inline-block;
  162.     background: #718096;
  163.     color: white;
  164.     font-size: 12px;
  165.     font-weight: 700;
  166.     padding: 4px 12px;
  167.     border-radius: 12px;
  168. }
  169. .parts-select {
  170.     flex: 1;
  171.     width: 100%;
  172.     max-width: 100%;
  173.     padding: 12px 16px;
  174.     font-size: 16px;
  175.     color: #2d3748 !important;
  176.     background-color: #f7fafc !important;
  177.     border: 2px solid #e2e8f0 !important;
  178.     border-radius: 12px !important;
  179.     transition: all 0.3s ease !important;
  180.     cursor: pointer;
  181.     box-sizing: border-box;
  182. }
  183. .parts-select:focus {
  184.     outline: none !important;
  185.     border-color: #667eea !important;
  186.     background-color: #ffffff !important;
  187.     box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
  188. }
  189. .parts-select option {
  190.     padding: 10px;
  191. }
  192. .parts-select option[value=\"\"] {
  193.     color: #a0aec0 !important;
  194.     font-style: italic;
  195. }
  196. .parts-select option:disabled {
  197.     color: #a0aec0 !important;
  198. }
  199. /* 見積もりサマリー(常時表示) */
  200. .estimate-summary {
  201.     background: linear-gradient(135deg, #f7fafc 0%, #edf2f7 100%);
  202.     border-radius: 16px;
  203.     padding: 30px;
  204.     margin-top: 40px;
  205. }
  206. .summary-header {
  207.     margin-bottom: 20px;
  208.     padding-bottom: 15px;
  209. }
  210. .summary-note {
  211.     font-size: 14px;
  212.     color: #1a202c;
  213.     font-weight: 600;
  214.     text-align: center;
  215. }
  216. .summary-items {
  217.     margin-bottom: 20px;
  218.     min-height: 100px;
  219. }
  220. .summary-empty {
  221.     text-align: center;
  222.     padding: 40px 20px;
  223.     color: #a0aec0;
  224.     font-size: 15px;
  225. }
  226. .summary-item {
  227.     display: block;
  228.     padding: 12px 0;
  229.     border-bottom: 1px solid #e2e8f0;
  230. }
  231. .summary-item-left {
  232.     width: 100%;
  233. }
  234. .summary-item-category {
  235.     font-size: 13px;
  236.     color: #718096;
  237.     margin-bottom: 4px;
  238. }
  239. .summary-item-name {
  240.     font-size: 15px;
  241.     font-weight: 600;
  242.     color: #2d3748;
  243. }
  244. .summary-total {
  245.     display: flex;
  246.     justify-content: space-between;
  247.     align-items: center;
  248.     padding: 24px 28px;
  249.     margin-top: 20px;
  250.     border-top: none;
  251.     background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  252.     border-radius: 12px;
  253.     box-shadow: 0 4px 12px rgba(168, 179, 255, 0.3);
  254. }
  255. .summary-total-label {
  256.     font-size: 22px;
  257.     font-weight: 700;
  258.     color: #ffffff;
  259. }
  260. .summary-total-price {
  261.     font-size: 36px;
  262.     font-weight: 800;
  263.     color: #ffffff;
  264.     text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  265. }
  266. .summary-total-price.hidden {
  267.     color: rgba(255, 255, 255, 0.4);
  268.     text-shadow: none;
  269. }
  270. /* 必須項目未選択警告 */
  271. .required-warning {
  272.     background: #fff5f5;
  273.     border: 2px solid #fc8181;
  274.     border-radius: 12px;
  275.     padding: 15px 20px;
  276.     margin-top: 20px;
  277.     text-align: center;
  278.     display: none;
  279. }
  280. .required-warning.active {
  281.     display: block;
  282. }
  283. .required-warning-text {
  284.     font-size: 14px;
  285.     font-weight: 600;
  286.     color: #742a2a;
  287. }
  288. /* お客様情報フォーム */
  289. .customer-form {
  290.     margin-top: 30px;
  291.     padding: 25px;
  292.     background: #f7fafc;
  293.     border-radius: 12px;
  294.     border: 1px solid #e2e8f0;
  295.     display: none;
  296. }
  297. .customer-form.active {
  298.     display: block;
  299.     animation: fadeIn 0.3s ease;
  300. }
  301. @keyframes fadeIn {
  302.     from { opacity: 0; transform: translateY(10px); }
  303.     to { opacity: 1; transform: translateY(0); }
  304. }
  305. .customer-form h3 {
  306.     font-size: 20px;
  307.     font-weight: 700;
  308.     color: #2d3748;
  309.     margin-bottom: 20px;
  310. }
  311. .form-group {
  312.     margin-bottom: 25px;
  313. }
  314. .form-row {
  315.     display: flex;
  316.     gap: 20px;
  317.     margin-bottom: 0;
  318. }
  319. .form-group-half {
  320.     flex: 1;
  321.     margin-bottom: 25px;
  322. }
  323. .form-label {
  324.     display: block;
  325.     font-size: 16px;
  326.     font-weight: 700;
  327.     color: #2d3748;
  328.     margin-bottom: 10px;
  329. }
  330. .form-label .required {
  331.     color: #e53e3e;
  332.     margin-left: 4px;
  333. }
  334. .form-control {
  335.     width: 100%;
  336.     padding: 14px 18px;
  337.     font-size: 16px;
  338.     color: #2d3748 !important;
  339.     background-color: #f7fafc !important;
  340.     border: 2px solid #e2e8f0 !important;
  341.     border-radius: 12px !important;
  342.     transition: all 0.3s ease !important;
  343. }
  344. .form-control:focus {
  345.     outline: none !important;
  346.     border-color: #667eea !important;
  347.     background-color: #ffffff !important;
  348.     box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
  349. }
  350. textarea.form-control {
  351.     min-height: 120px;
  352.     resize: vertical;
  353. }
  354. /* 送信ボタン */
  355. .submit-section {
  356.     margin-top: 40px;
  357.     text-align: center;
  358.     display: none;
  359. }
  360. .submit-section.active {
  361.     display: block;
  362.     animation: fadeIn 0.3s ease;
  363. }
  364. .submit-btn {
  365.     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  366.     color: #ffffff;
  367.     font-size: 18px;
  368.     font-weight: 700;
  369.     padding: 18px 60px;
  370.     border: none;
  371.     border-radius: 50px;
  372.     cursor: pointer;
  373.     transition: all 0.3s ease;
  374.     box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
  375. }
  376. .submit-btn:hover {
  377.     transform: translateY(-2px);
  378.     box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
  379. }
  380. .submit-btn:disabled {
  381.     background: #cbd5e0;
  382.     cursor: not-allowed;
  383.     box-shadow: none;
  384. }
  385. /* ローディング */
  386. .loading {
  387.     text-align: center;
  388.     padding: 40px;
  389.     display: none;
  390. }
  391. .loading.active {
  392.     display: block;
  393. }
  394. .loading-spinner {
  395.     border: 4px solid #e2e8f0;
  396.     border-top: 4px solid #667eea;
  397.     border-radius: 50%;
  398.     width: 50px;
  399.     height: 50px;
  400.     animation: spin 1s linear infinite;
  401.     margin: 0 auto 20px;
  402. }
  403. @keyframes spin {
  404.     0% { transform: rotate(0deg); }
  405.     100% { transform: rotate(360deg); }
  406. }
  407. /* 完了メッセージ */
  408. .complete-message {
  409.     text-align: center;
  410.     padding: 60px 40px;
  411.     display: none;
  412. }
  413. .complete-message.active {
  414.     display: block;
  415.     animation: fadeIn 0.5s ease;
  416. }
  417. .complete-icon {
  418.     font-size: 72px;
  419.     color: #48bb78;
  420.     margin-bottom: 20px;
  421. }
  422. .complete-title {
  423.     font-size: 28px;
  424.     font-weight: 700;
  425.     color: #2d3748;
  426.     margin-bottom: 15px;
  427. }
  428. .complete-text {
  429.     font-size: 16px;
  430.     color: #718096;
  431.     line-height: 1.8;
  432. }
  433. /* 注意事項 */
  434. .notice-text {
  435.     margin-top: 20px;
  436.     padding: 15px 20px;
  437.     background-color: #fffbeb;
  438.     border-left: 4px solid #f59e0b;
  439.     border-radius: 4px;
  440.     color: #92400e;
  441.     font-size: 14px;
  442.     line-height: 1.7;
  443. }
  444. /* フリーエリア */
  445. .nz-free-area {
  446.     max-width: 1200px;
  447.     margin: 40px auto;
  448.     padding: 0 20px;
  449.     text-align: center;
  450. }
  451. .nz-free-area h2 {
  452.     font-size: 28px;
  453.     font-weight: 700;
  454.     color: #2d3748;
  455.     margin-bottom: 20px;
  456.     text-align: center;
  457. }
  458. .nz-free-area h3 {
  459.     font-size: 22px;
  460.     font-weight: 600;
  461.     color: #4a5568;
  462.     margin: 30px 0 15px;
  463.     text-align: left;
  464. }
  465. .nz-free-area p {
  466.     font-size: 16px;
  467.     color: #718096;
  468.     line-height: 1.8;
  469.     margin-bottom: 15px;
  470.     text-align: left;
  471. }
  472. .nz-free-area img {
  473.     max-width: 100%;
  474.     height: auto;
  475.     border-radius: 8px;
  476.     margin: 20px 0;
  477. }
  478. .nz-free-area .btn {
  479.     display: inline-block;
  480.     width: 100%;
  481.     max-width: 400px;
  482.     padding: 18px 30px;
  483.     background: linear-gradient(135deg, #10b981 0%, #059669 100%);
  484.     color: white !important;
  485.     text-decoration: none;
  486.     border-radius: 16px;
  487.     font-weight: 700;
  488.     font-size: 18px;
  489.     text-align: center;
  490.     transition: all 0.3s ease;
  491.     border: none;
  492.     cursor: pointer;
  493.     box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
  494. }
  495. .nz-free-area .btn:hover {
  496.     background: linear-gradient(135deg, #059669 0%, #047857 100%);
  497.     transform: translateY(-2px);
  498.     box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
  499. }
  500. /* 購入セクション */
  501. .purchase-section {
  502.     margin-bottom: 40px;
  503. }
  504. .purchase-card {
  505.     background: #ffffff;
  506.     border: none;
  507.     border-radius: 0;
  508.     padding: 20px 0;
  509.     text-align: center;
  510. }
  511. .purchase-btn {
  512.     width: 100%;
  513.     max-width: 400px;
  514.     padding: 18px;
  515.     background: linear-gradient(135deg, #10b981 0%, #059669 100%);
  516.     color: white;
  517.     border: none;
  518.     border-radius: 12px;
  519.     font-size: 18px;
  520.     font-weight: 700;
  521.     cursor: pointer;
  522.     transition: all 0.3s ease;
  523.     box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
  524.     margin-bottom: 15px;
  525. }
  526. .purchase-btn:hover {
  527.     transform: translateY(-2px);
  528.     box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
  529. }
  530. .purchase-btn:active {
  531.     transform: translateY(0);
  532. }
  533. .purchase-notice {
  534.     margin-top: 15px;
  535.     padding: 15px;
  536.     background-color: #f0fdf4;
  537.     border-radius: 8px;
  538.     color: #065f46;
  539.     font-size: 14px;
  540.     line-height: 1.7;
  541. }
  542. /* スマホ用固定サマリー:PC/タブレットでは非表示 */
  543. .estimate-summary-sticky {
  544.     display: none;
  545. }
  546. /* ==================== レスポンシブ対応 ==================== */
  547. @media (max-width: 768px) {
  548.     /* コンテナ */
  549.     .estimate-container {
  550.         padding: 10px;
  551.     }
  552.     
  553.     /* ヘッダー */
  554.     .estimate-header {
  555.         padding: 20px 10px;
  556.         margin-bottom: 15px;
  557.     }
  558.     
  559.     .estimate-header h1 {
  560.         font-size: 20px;
  561.     }
  562.     
  563.     .estimate-header p {
  564.         font-size: 13px;
  565.     }
  566.     
  567.     /* カテゴリセクション */
  568.     .category-section {
  569.         padding: 15px;
  570.         margin-bottom: 12px;
  571.         width: 100%;
  572.         box-sizing: border-box;
  573.     }
  574.     
  575.     .category-row {
  576.         display: flex;
  577.         flex-direction: column;
  578.         gap: 10px;
  579.         width: 100%;
  580.         max-width: 100%;
  581.         box-sizing: border-box;
  582.     }
  583.     
  584.     .category-header {
  585.         flex-direction: row;
  586.         align-items: center;
  587.         gap: 10px;
  588.         margin-bottom: 0;
  589.         width: 100%;
  590.     }
  591.     
  592.     .category-name {
  593.         font-size: 16px;
  594.     }
  595.     
  596.     .required-badge {
  597.         font-size: 11px;
  598.         padding: 3px 8px;
  599.     }
  600.     
  601.     /* セレクトボックス */
  602.     .parts-select {
  603.         font-size: 15px;
  604.         padding: 12px 35px 12px 12px;
  605.         margin-top: 0;
  606.         width: 100% !important;
  607.         max-width: 100% !important;
  608.         flex: none;
  609.     }
  610.     
  611.     /* 見積もりサマリー */
  612.     .estimate-summary {
  613.         padding: 15px;
  614.         margin-bottom: 15px;
  615.     }
  616.     
  617.     .summary-note {
  618.         font-size: 12px;
  619.     }
  620.     
  621.     .summary-total {
  622.         flex-direction: row;
  623.         align-items: center;
  624.         gap: 15px;
  625.         padding: 12px 0;
  626.     }
  627.     
  628.     .summary-total-label {
  629.         font-size: 15px;
  630.     }
  631.     
  632.     .summary-total-price {
  633.         font-size: 24px;
  634.         text-align: right;
  635.     }
  636.     
  637.     /* 購入セクション */
  638.     .purchase-section {
  639.         margin-bottom: 15px;
  640.     }
  641.     
  642.     .purchase-card {
  643.         padding: 15px;
  644.     }
  645.     
  646.     .purchase-btn {
  647.         max-width: 100%;
  648.         font-size: 15px;
  649.         padding: 14px;
  650.     }
  651.     
  652.     .purchase-notice {
  653.         font-size: 12px;
  654.         padding: 10px;
  655.         margin-top: 10px;
  656.     }
  657.     
  658.     /* お客様情報フォーム */
  659.     .customer-form {
  660.         padding: 15px;
  661.         margin-top: 15px;
  662.     }
  663.     
  664.     .customer-form h3 {
  665.         font-size: 16px;
  666.         margin-bottom: 12px;
  667.     }
  668.     
  669.     .form-row {
  670.         flex-direction: column;
  671.         gap: 0;
  672.     }
  673.     
  674.     .form-group {
  675.         margin-bottom: 15px;
  676.     }
  677.     
  678.     .form-label {
  679.         font-size: 14px;
  680.         margin-bottom: 6px;
  681.     }
  682.     
  683.     .form-control {
  684.         padding: 10px 12px;
  685.         font-size: 15px;
  686.     }
  687.     
  688.     textarea.form-control {
  689.         min-height: 80px;
  690.     }
  691.     
  692.     /* 送信ボタン */
  693.     .submit-section {
  694.         padding: 15px;
  695.         margin-top: 15px;
  696.     }
  697.     
  698.     .submit-btn {
  699.         padding: 14px;
  700.         font-size: 15px;
  701.     }
  702.     
  703.     /* 必須警告 */
  704.     .required-warning {
  705.         padding: 10px;
  706.         font-size: 12px;
  707.         margin: 10px 0;
  708.     }
  709.     
  710.     /* フリーエリア */
  711.     .nz-free-area {
  712.         padding: 20px 15px;
  713.         font-size: 14px;
  714.     }
  715.     
  716.     .nz-free-area h2 {
  717.         font-size: 20px;
  718.     }
  719.     
  720.     .nz-free-area h3 {
  721.         font-size: 18px;
  722.     }
  723. }
  724. @media (max-width: 480px) {
  725.     /* さらに小さい画面用 */
  726.     .estimate-container {
  727.         padding: 8px;
  728.     }
  729.     
  730.     .estimate-header {
  731.         padding: 15px 8px;
  732.     }
  733.     
  734.     .estimate-header h1 {
  735.         font-size: 18px;
  736.     }
  737.     
  738.     .estimate-header p {
  739.         font-size: 12px;
  740.     }
  741.     
  742.     .category-section {
  743.         padding: 12px;
  744.         margin-bottom: 10px;
  745.     }
  746.     
  747.     .category-row {
  748.         display: flex;
  749.         flex-direction: column;
  750.         gap: 8px;
  751.     }
  752.     
  753.     .category-name {
  754.         font-size: 15px;
  755.     }
  756.     
  757.     .parts-select {
  758.         font-size: 14px;
  759.         padding: 10px 30px 10px 10px;
  760.         width: 100% !important;
  761.         max-width: 100% !important;
  762.         flex: none;
  763.     }
  764.     
  765.     .estimate-summary {
  766.         padding: 12px;
  767.     }
  768.     
  769.     .summary-title {
  770.         font-size: 15px;
  771.     }
  772.     
  773.     .summary-total {
  774.         padding: 16px 20px !important;
  775.         border-radius: 12px;
  776.     }
  777.     
  778.     .summary-total-label {
  779.         font-size: 18px !important;
  780.     }
  781.     
  782.     .summary-total-price {
  783.         font-size: 26px !important;
  784.     }
  785.     
  786.     /* スマホ用:下部固定サマリー(クローン) */
  787.     .estimate-summary-sticky {
  788.         display: none; /* 初期状態では非表示 */
  789.         position: fixed;
  790.         bottom: 0;
  791.         left: 0;
  792.         right: 0;
  793.         background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  794.         box-shadow: 0 -4px 12px rgba(168, 179, 255, 0.4);
  795.         z-index: 9999;
  796.         padding: 8px 16px;
  797.     }
  798.     
  799.     .estimate-summary-sticky.visible {
  800.         display: block !important; /* スクロールで表示 */
  801.     }
  802.     
  803.     .estimate-summary-sticky .summary-total {
  804.         margin: 0;
  805.         padding: 0;
  806.         border-top: none;
  807.         display: flex;
  808.         justify-content: space-between;
  809.         align-items: center;
  810.         background: transparent;
  811.         box-shadow: none;
  812.         border-radius: 0;
  813.     }
  814.     
  815.     .estimate-summary-sticky .summary-total-label {
  816.         font-size: 14px;
  817.         font-weight: 700;
  818.         color: #ffffff;
  819.     }
  820.     
  821.     .estimate-summary-sticky .summary-total-price {
  822.         font-size: 20px;
  823.         font-weight: 800;
  824.         color: #ffffff;
  825.         text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  826.     }
  827.     
  828.     /* コンテンツが固定サマリーに隠れないよう余白 */
  829.     body.sticky-summary-active {
  830.         padding-bottom: 50px;
  831.     }
  832.     
  833.     .purchase-card {
  834.         padding: 12px;
  835.     }
  836.     
  837.     .purchase-btn {
  838.         font-size: 14px;
  839.         padding: 12px;
  840.     }
  841.     
  842.     .customer-form {
  843.         padding: 12px;
  844.     }
  845.     
  846.     .form-group {
  847.         margin-bottom: 12px;
  848.     }
  849.     
  850.     .submit-section {
  851.         padding: 12px;
  852.     }
  853. }
  854. </style>
  855. <div class=\"nz-estimate-wrapper\">
  856.     <div class=\"nz-estimate-card\">
  857.         <div class=\"nz-estimate-header\">
  858.             <h1 class=\"nz-estimate-title\">
  859.                 ";
  860.         // line 895
  861.         if (twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'895$this->source); })()), "pageTitle", [], "any"falsefalsefalse895)) {
  862.             // line 896
  863.             echo "                    ";
  864.             echo twig_replace_filter(twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'896$this->source); })()), "pageTitle", [], "any"falsefalsefalse896), ["&lt;" => "<""&gt;" => ">""&quot;" => "\"""&#039;" => "'""&amp;" => "&"]);
  865.             echo "
  866.                 ";
  867.         } else {
  868.             // line 898
  869.             echo "                    見積もりシミュレーター
  870.                 ";
  871.         }
  872.         // line 900
  873.         echo "            </h1>
  874.             <div class=\"nz-estimate-description\">
  875.                 ";
  876.         // line 902
  877.         if (twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'902$this->source); })()), "pageDescription", [], "any"falsefalsefalse902)) {
  878.             // line 903
  879.             echo "                    ";
  880.             echo twig_replace_filter(twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'903$this->source); })()), "pageDescription", [], "any"falsefalsefalse903), ["&lt;" => "<""&gt;" => ">""&quot;" => "\"""&#039;" => "'""&amp;" => "&"]);
  881.             echo "
  882.                 ";
  883.         } else {
  884.             // line 905
  885.             echo "                    パーツを選択して、お見積もりを作成いただけます。
  886.                 ";
  887.         }
  888.         // line 907
  889.         echo "            </div>
  890.         </div>
  891.         <div class=\"nz-estimate-body\">
  892.             ";
  893.         // line 911
  894.         $context['_parent'] = $context;
  895.         $context['_seq'] = twig_ensure_traversable((isset($context["categoriesWithParts"]) || array_key_exists("categoriesWithParts"$context) ? $context["categoriesWithParts"] : (function () { throw new RuntimeError('Variable "categoriesWithParts" does not exist.'911$this->source); })()));
  896.         foreach ($context['_seq'] as $context["_key"] => $context["item"]) {
  897.             // line 912
  898.             echo "                <div class=\"category-section\" data-category-id=\"";
  899.             echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse912), "id", [], "any"falsefalsefalse912), "html"nulltrue);
  900.             echo "\">
  901.                     <div class=\"category-row\">
  902.                         <div class=\"category-header\">
  903.                             <h2 class=\"category-title\">";
  904.             // line 915
  905.             echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse915), "name", [], "any"falsefalsefalse915), "html"nulltrue);
  906.             echo "</h2>
  907.                             ";
  908.             // line 916
  909.             if (twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse916), "isRequired", [], "any"falsefalsefalse916)) {
  910.                 // line 917
  911.                 echo "                                <span class=\"category-required-badge\">必須</span>
  912.                             ";
  913.             } else {
  914.                 // line 919
  915.                 echo "                                <span class=\"category-optional-badge\">任意</span>
  916.                             ";
  917.             }
  918.             // line 921
  919.             echo "                        </div>
  920.                         
  921.                         ";
  922.             // line 924
  923.             echo "                        <select class=\"parts-select\" 
  924.                                 data-category-id=\"";
  925.             // line 925
  926.             echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse925), "id", [], "any"falsefalsefalse925), "html"nulltrue);
  927.             echo "\" 
  928.                                 data-required=\"";
  929.             // line 926
  930.             echo ((twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse926), "isRequired", [], "any"falsefalsefalse926)) ? ("1") : ("0"));
  931.             echo "\"
  932.                                 data-category-name=\"";
  933.             // line 927
  934.             echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse927), "name", [], "any"falsefalsefalse927), "html"nulltrue);
  935.             echo "\">
  936.                             <option value=\"\">選択してください</option>
  937.                             ";
  938.             // line 929
  939.             $context['_parent'] = $context;
  940.             $context['_seq'] = twig_ensure_traversable(twig_get_attribute($this->env$this->source$context["item"], "parts", [], "any"falsefalsefalse929));
  941.             foreach ($context['_seq'] as $context["_key"] => $context["parts"]) {
  942.                 // line 930
  943.                 echo "                                <option value=\"";
  944.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "id", [], "any"falsefalsefalse930), "html"nulltrue);
  945.                 echo "\" 
  946.                                         data-price=\"";
  947.                 // line 931
  948.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "price", [], "any"falsefalsefalse931), "html"nulltrue);
  949.                 echo "\"
  950.                                         data-name=\"";
  951.                 // line 932
  952.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "productName", [], "any"falsefalsefalse932), "html"nulltrue);
  953.                 echo "\"
  954.                                         data-child-category-id=\"";
  955.                 // line 933
  956.                 ((twig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse933)) ? (print (twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse933), "html"nulltrue))) : (print ("")));
  957.                 echo "\"
  958.                                         data-child-filter-keywords=\"";
  959.                 // line 934
  960.                 ((twig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse934)) ? (print (twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse934), "html"nulltrue))) : (print ("")));
  961.                 echo "\">
  962.                                     ";
  963.                 // line 935
  964.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "productName", [], "any"falsefalsefalse935), "html"nulltrue);
  965.                 echo "
  966.                                 </option>
  967.                             ";
  968.             }
  969.             $_parent $context['_parent'];
  970.             unset($context['_seq'], $context['_iterated'], $context['_key'], $context['parts'], $context['_parent'], $context['loop']);
  971.             $context array_intersect_key($context$_parent) + $_parent;
  972.             // line 938
  973.             echo "                        </select>
  974.                     </div>
  975.                 </div>
  976.             ";
  977.         }
  978.         $_parent $context['_parent'];
  979.         unset($context['_seq'], $context['_iterated'], $context['_key'], $context['item'], $context['_parent'], $context['loop']);
  980.         $context array_intersect_key($context$_parent) + $_parent;
  981.         // line 942
  982.         echo "            
  983.             ";
  984.         // line 944
  985.         echo "            <script type=\"application/json\" id=\"allPartsData\">
  986.             [
  987.                 ";
  988.         // line 946
  989.         $context['_parent'] = $context;
  990.         $context['_seq'] = twig_ensure_traversable((isset($context["categoriesWithParts"]) || array_key_exists("categoriesWithParts"$context) ? $context["categoriesWithParts"] : (function () { throw new RuntimeError('Variable "categoriesWithParts" does not exist.'946$this->source); })()));
  991.         $context['loop'] = [
  992.           'parent' => $context['_parent'],
  993.           'index0' => 0,
  994.           'index'  => 1,
  995.           'first'  => true,
  996.         ];
  997.         if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  998.             $length count($context['_seq']);
  999.             $context['loop']['revindex0'] = $length 1;
  1000.             $context['loop']['revindex'] = $length;
  1001.             $context['loop']['length'] = $length;
  1002.             $context['loop']['last'] = === $length;
  1003.         }
  1004.         foreach ($context['_seq'] as $context["_key"] => $context["item"]) {
  1005.             // line 947
  1006.             echo "                    ";
  1007.             $context['_parent'] = $context;
  1008.             $context['_seq'] = twig_ensure_traversable(twig_get_attribute($this->env$this->source$context["item"], "parts", [], "any"falsefalsefalse947));
  1009.             $context['loop'] = [
  1010.               'parent' => $context['_parent'],
  1011.               'index0' => 0,
  1012.               'index'  => 1,
  1013.               'first'  => true,
  1014.             ];
  1015.             if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  1016.                 $length count($context['_seq']);
  1017.                 $context['loop']['revindex0'] = $length 1;
  1018.                 $context['loop']['revindex'] = $length;
  1019.                 $context['loop']['length'] = $length;
  1020.                 $context['loop']['last'] = === $length;
  1021.             }
  1022.             foreach ($context['_seq'] as $context["_key"] => $context["parts"]) {
  1023.                 // line 948
  1024.                 echo "                    {
  1025.                         \"id\": ";
  1026.                 // line 949
  1027.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "id", [], "any"falsefalsefalse949), "html"nulltrue);
  1028.                 echo ",
  1029.                         \"category_id\": ";
  1030.                 // line 950
  1031.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse950), "id", [], "any"falsefalsefalse950), "html"nulltrue);
  1032.                 echo ",
  1033.                         \"category_name\": ";
  1034.                 // line 951
  1035.                 echo json_encode(twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse951), "name", [], "any"falsefalsefalse951));
  1036.                 echo ",
  1037.                         \"name\": ";
  1038.                 // line 952
  1039.                 echo json_encode(twig_get_attribute($this->env$this->source$context["parts"], "productName", [], "any"falsefalsefalse952));
  1040.                 echo ",
  1041.                         \"model_number\": ";
  1042.                 // line 953
  1043.                 echo json_encode(((twig_get_attribute($this->env$this->source$context["parts"], "modelNumber", [], "any"truetruefalse953)) ? (_twig_default_filter(twig_get_attribute($this->env$this->source$context["parts"], "modelNumber", [], "any"falsefalsefalse953), "")) : ("")));
  1044.                 echo ",
  1045.                         \"price\": ";
  1046.                 // line 954
  1047.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "price", [], "any"falsefalsefalse954), "html"nulltrue);
  1048.                 echo ",
  1049.                         \"child_category_id\": ";
  1050.                 // line 955
  1051.                 echo ((twig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse955)) ? (json_encode(twig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse955))) : ("null"));
  1052.                 echo ",
  1053.                         \"child_filter_keywords\": ";
  1054.                 // line 956
  1055.                 echo ((twig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse956)) ? (json_encode(twig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse956))) : ("null"));
  1056.                 echo "
  1057.                     }";
  1058.                 // line 957
  1059.                 if (( !twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["loop"], "parent", [], "any"falsefalsefalse957), "loop", [], "any"falsefalsefalse957), "last", [], "any"falsefalsefalse957) ||  !twig_get_attribute($this->env$this->source$context["loop"], "last", [], "any"falsefalsefalse957))) {
  1060.                     echo ",";
  1061.                 }
  1062.                 // line 958
  1063.                 echo "                    ";
  1064.                 ++$context['loop']['index0'];
  1065.                 ++$context['loop']['index'];
  1066.                 $context['loop']['first'] = false;
  1067.                 if (isset($context['loop']['length'])) {
  1068.                     --$context['loop']['revindex0'];
  1069.                     --$context['loop']['revindex'];
  1070.                     $context['loop']['last'] = === $context['loop']['revindex0'];
  1071.                 }
  1072.             }
  1073.             $_parent $context['_parent'];
  1074.             unset($context['_seq'], $context['_iterated'], $context['_key'], $context['parts'], $context['_parent'], $context['loop']);
  1075.             $context array_intersect_key($context$_parent) + $_parent;
  1076.             // line 959
  1077.             echo "                ";
  1078.             ++$context['loop']['index0'];
  1079.             ++$context['loop']['index'];
  1080.             $context['loop']['first'] = false;
  1081.             if (isset($context['loop']['length'])) {
  1082.                 --$context['loop']['revindex0'];
  1083.                 --$context['loop']['revindex'];
  1084.                 $context['loop']['last'] = === $context['loop']['revindex0'];
  1085.             }
  1086.         }
  1087.         $_parent $context['_parent'];
  1088.         unset($context['_seq'], $context['_iterated'], $context['_key'], $context['item'], $context['_parent'], $context['loop']);
  1089.         $context array_intersect_key($context$_parent) + $_parent;
  1090.         // line 960
  1091.         echo "            ]
  1092.             </script>
  1093.             
  1094.             ";
  1095.         // line 964
  1096.         echo "            <script type=\"application/json\" id=\"allCategoriesData\">
  1097.             [
  1098.                 ";
  1099.         // line 966
  1100.         $context['_parent'] = $context;
  1101.         $context['_seq'] = twig_ensure_traversable((isset($context["allCategoriesWithParts"]) || array_key_exists("allCategoriesWithParts"$context) ? $context["allCategoriesWithParts"] : (function () { throw new RuntimeError('Variable "allCategoriesWithParts" does not exist.'966$this->source); })()));
  1102.         $context['loop'] = [
  1103.           'parent' => $context['_parent'],
  1104.           'index0' => 0,
  1105.           'index'  => 1,
  1106.           'first'  => true,
  1107.         ];
  1108.         if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  1109.             $length count($context['_seq']);
  1110.             $context['loop']['revindex0'] = $length 1;
  1111.             $context['loop']['revindex'] = $length;
  1112.             $context['loop']['length'] = $length;
  1113.             $context['loop']['last'] = === $length;
  1114.         }
  1115.         foreach ($context['_seq'] as $context["_key"] => $context["item"]) {
  1116.             // line 967
  1117.             echo "                {
  1118.                     \"id\": ";
  1119.             // line 968
  1120.             echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse968), "id", [], "any"falsefalsefalse968), "html"nulltrue);
  1121.             echo ",
  1122.                     \"name\": ";
  1123.             // line 969
  1124.             echo json_encode(twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse969), "name", [], "any"falsefalsefalse969));
  1125.             echo ",
  1126.                     \"is_required\": ";
  1127.             // line 970
  1128.             echo ((twig_get_attribute($this->env$this->sourcetwig_get_attribute($this->env$this->source$context["item"], "category", [], "any"falsefalsefalse970), "isRequired", [], "any"falsefalsefalse970)) ? ("true") : ("false"));
  1129.             echo ",
  1130.                     \"parts\": [
  1131.                         ";
  1132.             // line 972
  1133.             $context['_parent'] = $context;
  1134.             $context['_seq'] = twig_ensure_traversable(twig_get_attribute($this->env$this->source$context["item"], "parts", [], "any"falsefalsefalse972));
  1135.             $context['loop'] = [
  1136.               'parent' => $context['_parent'],
  1137.               'index0' => 0,
  1138.               'index'  => 1,
  1139.               'first'  => true,
  1140.             ];
  1141.             if (is_array($context['_seq']) || (is_object($context['_seq']) && $context['_seq'] instanceof \Countable)) {
  1142.                 $length count($context['_seq']);
  1143.                 $context['loop']['revindex0'] = $length 1;
  1144.                 $context['loop']['revindex'] = $length;
  1145.                 $context['loop']['length'] = $length;
  1146.                 $context['loop']['last'] = === $length;
  1147.             }
  1148.             foreach ($context['_seq'] as $context["_key"] => $context["parts"]) {
  1149.                 // line 973
  1150.                 echo "                        {
  1151.                             \"id\": ";
  1152.                 // line 974
  1153.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "id", [], "any"falsefalsefalse974), "html"nulltrue);
  1154.                 echo ",
  1155.                             \"name\": ";
  1156.                 // line 975
  1157.                 echo json_encode(twig_get_attribute($this->env$this->source$context["parts"], "productName", [], "any"falsefalsefalse975));
  1158.                 echo ",
  1159.                             \"model_number\": ";
  1160.                 // line 976
  1161.                 echo json_encode(((twig_get_attribute($this->env$this->source$context["parts"], "modelNumber", [], "any"truetruefalse976)) ? (_twig_default_filter(twig_get_attribute($this->env$this->source$context["parts"], "modelNumber", [], "any"falsefalsefalse976), "")) : ("")));
  1162.                 echo ",
  1163.                             \"price\": ";
  1164.                 // line 977
  1165.                 echo twig_escape_filter($this->envtwig_get_attribute($this->env$this->source$context["parts"], "price", [], "any"falsefalsefalse977), "html"nulltrue);
  1166.                 echo ",
  1167.                             \"child_category_id\": ";
  1168.                 // line 978
  1169.                 echo ((twig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse978)) ? (json_encode(twig_get_attribute($this->env$this->source$context["parts"], "childCategoryId", [], "any"falsefalsefalse978))) : ("null"));
  1170.                 echo ",
  1171.                             \"child_filter_keywords\": ";
  1172.                 // line 979
  1173.                 echo ((twig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse979)) ? (json_encode(twig_get_attribute($this->env$this->source$context["parts"], "childFilterKeywords", [], "any"falsefalsefalse979))) : ("null"));
  1174.                 echo "
  1175.                         }";
  1176.                 // line 980
  1177.                 if ( !twig_get_attribute($this->env$this->source$context["loop"], "last", [], "any"falsefalsefalse980)) {
  1178.                     echo ",";
  1179.                 }
  1180.                 // line 981
  1181.                 echo "                        ";
  1182.                 ++$context['loop']['index0'];
  1183.                 ++$context['loop']['index'];
  1184.                 $context['loop']['first'] = false;
  1185.                 if (isset($context['loop']['length'])) {
  1186.                     --$context['loop']['revindex0'];
  1187.                     --$context['loop']['revindex'];
  1188.                     $context['loop']['last'] = === $context['loop']['revindex0'];
  1189.                 }
  1190.             }
  1191.             $_parent $context['_parent'];
  1192.             unset($context['_seq'], $context['_iterated'], $context['_key'], $context['parts'], $context['_parent'], $context['loop']);
  1193.             $context array_intersect_key($context$_parent) + $_parent;
  1194.             // line 982
  1195.             echo "                    ]
  1196.                 }";
  1197.             // line 983
  1198.             if ( !twig_get_attribute($this->env$this->source$context["loop"], "last", [], "any"falsefalsefalse983)) {
  1199.                 echo ",";
  1200.             }
  1201.             // line 984
  1202.             echo "                ";
  1203.             ++$context['loop']['index0'];
  1204.             ++$context['loop']['index'];
  1205.             $context['loop']['first'] = false;
  1206.             if (isset($context['loop']['length'])) {
  1207.                 --$context['loop']['revindex0'];
  1208.                 --$context['loop']['revindex'];
  1209.                 $context['loop']['last'] = === $context['loop']['revindex0'];
  1210.             }
  1211.         }
  1212.         $_parent $context['_parent'];
  1213.         unset($context['_seq'], $context['_iterated'], $context['_key'], $context['item'], $context['_parent'], $context['loop']);
  1214.         $context array_intersect_key($context$_parent) + $_parent;
  1215.         // line 985
  1216.         echo "            ]
  1217.             </script>
  1218.             <!-- 見積もりサマリー(常時表示) -->
  1219.             <div class=\"estimate-summary\">
  1220.                 <div class=\"summary-header\">
  1221.                     <div class=\"summary-note\">※ 必須項目をすべて選択すると金額が表示されます</div>
  1222.                 </div>
  1223.                 
  1224.                 <!-- 必須項目未選択警告 -->
  1225.                 <div class=\"required-warning\" id=\"requiredWarning\">
  1226.                     <div class=\"required-warning-text\">
  1227.                         ⚠️ 必須カテゴリをすべて選択してください
  1228.                     </div>
  1229.                 </div>
  1230.                 
  1231.                 <div class=\"summary-total\">
  1232.                     <div class=\"summary-total-label\">合計金額</div>
  1233.                     <div class=\"summary-total-price\" id=\"totalPriceArea\">
  1234.                         ¥<span id=\"totalPrice\">---</span>
  1235.                     </div>
  1236.                 </div>
  1237.             </div>
  1238.             <!-- スマホ用:下部固定サマリー -->
  1239.             <div class=\"estimate-summary-sticky\" id=\"stickySummary\">
  1240.                 <div class=\"summary-total\">
  1241.                     <div class=\"summary-total-label\">合計金額</div>
  1242.                     <div class=\"summary-total-price\">
  1243.                         ¥<span id=\"stickyTotalPrice\">---</span>
  1244.                     </div>
  1245.                 </div>
  1246.             </div>
  1247.             <!-- 今すぐ購入セクション -->
  1248.             <div class=\"purchase-section\" id=\"purchaseSection\" style=\"display: none;\">
  1249.                 <div class=\"purchase-card\">
  1250.                     <button type=\"button\" class=\"purchase-btn\" id=\"purchaseBtn\">
  1251.                         🛒 すぐに購入
  1252.                     </button>
  1253.                     
  1254.                     ";
  1255.         // line 1026
  1256.         if (twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1026$this->source); })()), "purchaseNoticeText", [], "any"falsefalsefalse1026)) {
  1257.             // line 1027
  1258.             echo "                    <div class=\"purchase-notice\">
  1259.                         ";
  1260.             // line 1028
  1261.             echo twig_nl2br(twig_escape_filter($this->envtwig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1028$this->source); })()), "purchaseNoticeText", [], "any"falsefalsefalse1028), "html"nulltrue));
  1262.             echo "
  1263.                     </div>
  1264.                     ";
  1265.         }
  1266.         // line 1031
  1267.         echo "                </div>
  1268.             </div>
  1269.             <!-- お客様情報フォーム -->
  1270.             <div class=\"customer-form\" id=\"customerForm\">
  1271.                 <h3>お客様情報</h3>
  1272.                 <form id=\"estimateForm\">
  1273.                     <div class=\"form-row\">
  1274.                         <div class=\"form-group form-group-half\">
  1275.                             <label class=\"form-label\">
  1276.                                 お名前<span class=\"required\">*</span>
  1277.                             </label>
  1278.                             <input type=\"text\" name=\"name\" class=\"form-control\" required>
  1279.                         </div>
  1280.                         
  1281.                         <div class=\"form-group form-group-half\">
  1282.                             <label class=\"form-label\">
  1283.                                 電話番号<span class=\"required\">*</span>
  1284.                             </label>
  1285.                             <input type=\"tel\" name=\"tel\" class=\"form-control\" required>
  1286.                         </div>
  1287.                     </div>
  1288.                     
  1289.                     <div class=\"form-group\">
  1290.                         <label class=\"form-label\">
  1291.                             メールアドレス<span class=\"required\">*</span>
  1292.                         </label>
  1293.                         <input type=\"email\" name=\"email\" class=\"form-control\" required>
  1294.                     </div>
  1295.                     
  1296.                     <div class=\"form-group\">
  1297.                         <label class=\"form-label\">ご要望・備考</label>
  1298.                         <textarea name=\"message\" class=\"form-control\" rows=\"3\"></textarea>
  1299.                     </div>
  1300.                 </form>
  1301.             </div>
  1302.             <!-- 送信ボタン -->
  1303.             <div class=\"submit-section\" id=\"submitSection\">
  1304.                 <button type=\"button\" class=\"submit-btn\" id=\"submitBtn\">
  1305.                     見積もり依頼を送信
  1306.                 </button>
  1307.                 
  1308.                 ";
  1309.         // line 1074
  1310.         if (twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1074$this->source); })()), "noticeText", [], "any"falsefalsefalse1074)) {
  1311.             // line 1075
  1312.             echo "                <div class=\"notice-text\">
  1313.                     ";
  1314.             // line 1076
  1315.             echo twig_nl2br(twig_escape_filter($this->envtwig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1076$this->source); })()), "noticeText", [], "any"falsefalsefalse1076), "html"nulltrue));
  1316.             echo "
  1317.                 </div>
  1318.                 ";
  1319.         }
  1320.         // line 1079
  1321.         echo "            </div>
  1322.             <!-- ローディング -->
  1323.             <div class=\"loading\" id=\"loading\">
  1324.                 <div class=\"loading-spinner\"></div>
  1325.                 <p>送信中...</p>
  1326.             </div>
  1327.             <!-- 完了メッセージ -->
  1328.             <div class=\"complete-message\" id=\"completeMessage\">
  1329.                 <div class=\"complete-icon\">✓</div>
  1330.                 <div class=\"complete-title\">送信完了</div>
  1331.                 <div class=\"complete-text\">
  1332.                     お見積もり依頼を受け付けました。<br>
  1333.                     担当者より折り返しご連絡いたします。
  1334.                 </div>
  1335.             </div>
  1336.         </div>
  1337.     </div>
  1338. </div>
  1339. <!-- フリーエリア -->
  1340. ";
  1341.         // line 1101
  1342.         if (twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1101$this->source); })()), "freeAreaHtml", [], "any"falsefalsefalse1101)) {
  1343.             // line 1102
  1344.             echo "<div class=\"nz-free-area\">
  1345.     ";
  1346.             // line 1103
  1347.             echo twig_get_attribute($this->env$this->source, (isset($context["config"]) || array_key_exists("config"$context) ? $context["config"] : (function () { throw new RuntimeError('Variable "config" does not exist.'1103$this->source); })()), "freeAreaHtml", [], "any"falsefalsefalse1103);
  1348.             echo "
  1349. </div>
  1350. ";
  1351.         }
  1352.         // line 1106
  1353.         echo "
  1354. <script>
  1355. \$(function() {
  1356.     let selectedParts = [];
  1357.     
  1358.     // 内部料金設定(顧客には非表示)
  1359.     const additionalCharge = ";
  1360.         // line 1112
  1361.         echo twig_escape_filter($this->env, ((twig_get_attribute($this->env$this->source, ($context["config"] ?? null), "additionalCharge", [], "any"truetruefalse1112)) ? (_twig_default_filter(twig_get_attribute($this->env$this->source, ($context["config"] ?? null), "additionalCharge", [], "any"falsefalsefalse1112), 0)) : (0)), "html"nulltrue);
  1362.         echo ";
  1363.     const discountRate = ";
  1364.         // line 1113
  1365.         echo twig_escape_filter($this->env, ((twig_get_attribute($this->env$this->source, ($context["config"] ?? null), "discountRate", [], "any"truetruefalse1113)) ? (_twig_default_filter(twig_get_attribute($this->env$this->source, ($context["config"] ?? null), "discountRate", [], "any"falsefalsefalse1113), 0)) : (0)), "html"nulltrue);
  1366.         echo ";
  1367.     
  1368.     // データ読み込み
  1369.     const allParts = JSON.parse(\$('#allPartsData').text());
  1370.     const allCategories = JSON.parse(\$('#allCategoriesData').text());
  1371.     
  1372.     // 必須カテゴリを取得
  1373.     const requiredCategories = [];
  1374.     allCategories.forEach(function(cat) {
  1375.         if (cat.is_required) {
  1376.             requiredCategories.push(cat.id);
  1377.         }
  1378.     });
  1379.     
  1380.     // パーツ選択時の処理
  1381.     \$(document).on('change', '.parts-select', function() {
  1382.         const \$select = \$(this);
  1383.         const partsId = \$select.val();
  1384.         const categoryId = \$select.data('category-id');
  1385.         const \$currentSection = \$select.closest('.category-section');
  1386.         
  1387.         // 現在のセクションの後続の子カテゴリを削除(アニメーション付き)
  1388.         \$currentSection.nextAll('.category-section.child-category').each(function() {
  1389.             const \$childSection = \$(this);
  1390.             const parentId = \$childSection.data('parent-id');
  1391.             
  1392.             // 現在のカテゴリの子カテゴリのみ削除
  1393.             if (parentId == categoryId) {
  1394.                 \$childSection.removeClass('active').fadeOut(200, function() {
  1395.                     \$(this).remove();
  1396.                 });
  1397.             }
  1398.         });
  1399.         
  1400.         if (!partsId) {
  1401.             updateSummary();
  1402.             return;
  1403.         }
  1404.         
  1405.         // 選択したパーツ情報を取得
  1406.         const parts = allParts.find(p => p.id == partsId);
  1407.         
  1408.         if (!parts) {
  1409.             updateSummary();
  1410.             return;
  1411.         }
  1412.         
  1413.         // 子カテゴリがあるか確認(カンマ区切りで複数対応)
  1414.         if (parts.child_category_id) {
  1415.             const childCategoryIds = parts.child_category_id.toString().split(',').map(id => parseInt(id.trim())).filter(id => id > 0);
  1416.             
  1417.             console.log('========================================');
  1418.             console.log('選択されたパーツ:', parts.name);
  1419.             console.log('子カテゴリID:', childCategoryIds);
  1420.             console.log('フィルタキーワード:', parts.child_filter_keywords);
  1421.             
  1422.             childCategoryIds.forEach(function(childCatId) {
  1423.                 const childCategory = allCategories.find(c => c.id == childCatId);
  1424.                 
  1425.                 console.log('--- 子カテゴリID:', childCatId, '---');
  1426.                 console.log('子カテゴリ名:', childCategory ? childCategory.name : 'NOT FOUND');
  1427.                 
  1428.                 if (childCategory && childCategory.parts.length > 0) {
  1429.                     let filteredParts = childCategory.parts;
  1430.                     
  1431.                     // フィルタキーワードがある場合は絞り込み
  1432.                     if (parts.child_filter_keywords) {
  1433.                         const keywords = parts.child_filter_keywords.split(',').map(k => k.trim().toLowerCase());
  1434.                         
  1435.                         console.log('=== フィルタ実行 ===');
  1436.                         console.log('子カテゴリ:', childCategory.name);
  1437.                         console.log('フィルタキーワード:', keywords);
  1438.                         console.log('全パーツ数:', childCategory.parts.length);
  1439.                         
  1440.                         filteredParts = childCategory.parts.filter(p => {
  1441.                             const searchText = (p.name + ' ' + (p.model_number || '')).toLowerCase();
  1442.                             const matched = keywords.some(kw => searchText.includes(kw));
  1443.                             console.log(`  - \${p.name}: \${matched ? 'マッチ' : '除外'} (検索文字: \${searchText})`);
  1444.                             return matched;
  1445.                         });
  1446.                         
  1447.                         console.log('フィルタ後パーツ数:', filteredParts.length);
  1448.                     }
  1449.                     
  1450.                     // フィルタ後のパーツがある場合のみ子カテゴリを表示
  1451.                     if (filteredParts.length > 0) {
  1452.                         // 子カテゴリセクションを作成
  1453.                         createChildCategorySection({
  1454.                             ...childCategory,
  1455.                             parts: filteredParts
  1456.                         }, categoryId);
  1457.                     }
  1458.                 }
  1459.             });
  1460.         }
  1461.         
  1462.         updateSummary();
  1463.     });
  1464.     
  1465.     // 子カテゴリセクション作成(セレクトボックス形式)
  1466.     function createChildCategorySection(category, parentCategoryId) {
  1467.         const \$parentSection = \$(`.category-section[data-category-id=\"\${parentCategoryId}\"]`);
  1468.         
  1469.         const childHtml = `
  1470.             <div class=\"category-section child-category\" data-category-id=\"\${category.id}\" data-parent-id=\"\${parentCategoryId}\">
  1471.                 <div class=\"category-row\">
  1472.                     <div class=\"category-header\">
  1473.                         <h2 class=\"category-title child\">└ \${category.name}</h2>
  1474.                         \${category.is_required ? '<span class=\"category-required-badge\">必須</span>' : '<span class=\"category-optional-badge\">任意</span>'}
  1475.                     </div>
  1476.                     
  1477.                     <select class=\"parts-select\" 
  1478.                             data-category-id=\"\${category.id}\" 
  1479.                             data-parent-category-id=\"\${parentCategoryId}\"
  1480.                             data-required=\"\${category.is_required ? '1' : '0'}\"
  1481.                             data-category-name=\"\${category.name}\">
  1482.                         \${category.parts.map((p, index) => `
  1483.                             <option value=\"\${p.id}\" 
  1484.                                     data-price=\"\${p.price}\"
  1485.                                     data-name=\"\${p.name}\"
  1486.                                     data-child-category-id=\"\${p.child_category_id || ''}\"
  1487.                                     data-child-filter-keywords=\"\${p.child_filter_keywords || ''}\"
  1488.                                     \${index === 0 ? 'selected' : ''}>
  1489.                                 \${p.name}
  1490.                             </option>
  1491.                         `).join('')}
  1492.                     </select>
  1493.                 </div>
  1494.             </div>
  1495.         `;
  1496.         
  1497.         \$parentSection.after(childHtml);
  1498.         
  1499.         // 挿入後、activeクラスを追加してスライド表示
  1500.         setTimeout(function() {
  1501.             \$(`.child-category[data-parent-id=\"\${parentCategoryId}\"]`).addClass('active');
  1502.         }, 10);
  1503.     }
  1504.     
  1505.     // サマリー更新
  1506.     function updateSummary() {
  1507.         const items = [];
  1508.         const selectedCategories = new Set();
  1509.         let total = 0;
  1510.         
  1511.         // すべてのセレクトボックスを取得
  1512.         \$('.parts-select').each(function() {
  1513.             const \$select = \$(this);
  1514.             const value = \$select.val();
  1515.             
  1516.             if (value && value !== '') {
  1517.                 const selectedOption = \$select.find('option:selected');
  1518.                 const categoryId = \$select.data('category-id');
  1519.                 const parentCategoryId = \$select.data('parent-category-id');
  1520.                 const categoryName = \$select.data('category-name');
  1521.                 const price = Math.floor(parseFloat(selectedOption.data('price'))); // 小数点以下切り捨て
  1522.                 const name = selectedOption.data('name');
  1523.                 
  1524.                 items.push({
  1525.                     parts_id: value,
  1526.                     category_name: categoryName,
  1527.                     name: name,
  1528.                     price: price
  1529.                 });
  1530.                 
  1531.                 // 必須カテゴリの判定: 子カテゴリの場合は親カテゴリIDを、そうでない場合は自身のIDを使用
  1532.                 const checkCategoryId = parentCategoryId || categoryId;
  1533.                 if (checkCategoryId) {
  1534.                     selectedCategories.add(parseInt(checkCategoryId));
  1535.                 }
  1536.                 total += price;
  1537.             }
  1538.         });
  1539.         
  1540.         selectedParts = items;
  1541.         
  1542.         // 必須カテゴリがすべて選択されているかチェック
  1543.         const allRequiredSelected = requiredCategories.every(catId => selectedCategories.has(catId));
  1544.         
  1545.         if (items.length > 0) {
  1546.             // 合計金額の表示
  1547.             if (allRequiredSelected) {
  1548.                 // 内部料金計算: 合計 = パーツ合計 × (1 - 割引率/100) + 追加料金
  1549.                 let finalTotal = total * (1 - discountRate / 100) + additionalCharge;
  1550.                 finalTotal = Math.round(finalTotal); // 四捨五入
  1551.                 
  1552.                 \$('#totalPrice').text(finalTotal.toLocaleString());
  1553.                 \$('#stickyTotalPrice').text(finalTotal.toLocaleString()); // 固定サマリーも更新
  1554.                 \$('#totalPriceArea').removeClass('hidden');
  1555.                 \$('#requiredWarning').removeClass('active');
  1556.                 \$('#customerForm').addClass('active');
  1557.                 \$('#submitSection').addClass('active');
  1558.                 \$('#purchaseSection').show();
  1559.             } else {
  1560.                 \$('#totalPrice').text('---');
  1561.                 \$('#stickyTotalPrice').text('---'); // 固定サマリーもクリア
  1562.                 \$('#totalPriceArea').addClass('hidden');
  1563.                 \$('#requiredWarning').addClass('active');
  1564.                 \$('#customerForm').removeClass('active');
  1565.                 \$('#submitSection').removeClass('active');
  1566.                 \$('#purchaseSection').hide();
  1567.             }
  1568.         } else {
  1569.             \$('#totalPrice').text('---');
  1570.             \$('#stickyTotalPrice').text('---'); // 固定サマリーもクリア
  1571.             \$('#totalPriceArea').addClass('hidden');
  1572.             \$('#requiredWarning').removeClass('active');
  1573.             \$('#customerForm').removeClass('active');
  1574.             \$('#submitSection').removeClass('active');
  1575.             \$('#purchaseSection').hide();
  1576.         }
  1577.     }
  1578.     
  1579.     // 送信
  1580.     \$('#submitBtn').on('click', function() {
  1581.         if (selectedParts.length === 0) {
  1582.             alert('パーツを選択してください。');
  1583.             return;
  1584.         }
  1585.         
  1586.         const form = \$('#estimateForm')[0];
  1587.         if (!form.checkValidity()) {
  1588.             form.reportValidity();
  1589.             return;
  1590.         }
  1591.         
  1592.         const formData = new FormData(form);
  1593.         formData.append('items', JSON.stringify(selectedParts));
  1594.         
  1595.         \$('#submitBtn').prop('disabled', true);
  1596.         \$('#loading').addClass('active');
  1597.         
  1598.         \$.ajax({
  1599.             url: '";
  1600.         // line 1346
  1601.         echo $this->extensions['Symfony\Bridge\Twig\Extension\RoutingExtension']->getUrl("nz_estimate_simulator_send");
  1602.         echo "',
  1603.             type: 'POST',
  1604.             data: formData,
  1605.             processData: false,
  1606.             contentType: false,
  1607.             headers: {
  1608.                 'x-csrf-token': \$('meta[name=\"x-csrf-token\"]').attr('content')
  1609.             },
  1610.             success: function(response) {
  1611.                 \$('#loading').removeClass('active');
  1612.                 
  1613.                 if (response.success) {
  1614.                     \$('.category-section, .estimate-summary, .customer-form, .submit-section').hide();
  1615.                     \$('#completeMessage').addClass('active');
  1616.                 } else {
  1617.                     alert(response.message || '送信に失敗しました。');
  1618.                     \$('#submitBtn').prop('disabled', false);
  1619.                 }
  1620.             },
  1621.             error: function(xhr, status, error) {
  1622.                 \$('#loading').removeClass('active');
  1623.                 
  1624.                 let errorMessage = '送信中にエラーが発生しました。';
  1625.                 
  1626.                 if (xhr.responseJSON && xhr.responseJSON.message) {
  1627.                     errorMessage = xhr.responseJSON.message;
  1628.                 } else if (xhr.status === 0) {
  1629.                     errorMessage = 'ネットワークエラーが発生しました。インターネット接続を確認してください。';
  1630.                 } else if (xhr.status === 500) {
  1631.                     errorMessage = 'サーバーエラーが発生しました。しばらく時間をおいて再度お試しください。';
  1632.                 } else if (xhr.status === 403) {
  1633.                     errorMessage = 'セキュリティエラーが発生しました。ページを再読み込みして再度お試しください。';
  1634.                 }
  1635.                 
  1636.                 alert(errorMessage);
  1637.                 console.error('Error details:', {
  1638.                     status: xhr.status,
  1639.                     statusText: xhr.statusText,
  1640.                     responseText: xhr.responseText,
  1641.                     error: error
  1642.                 });
  1643.                 
  1644.                 \$('#submitBtn').prop('disabled', false);
  1645.             }
  1646.         });
  1647.     });
  1648.     // 今すぐ購入ボタン
  1649.     \$('#purchaseBtn').on('click', function(e) {
  1650.         e.preventDefault();
  1651.         
  1652.         console.log('購入ボタンがクリックされました');
  1653.         
  1654.         if (selectedParts.length === 0) {
  1655.             alert('パーツが選択されていません');
  1656.             return;
  1657.         }
  1658.         // パーツ合計を計算
  1659.         const partsTotal = Math.floor(selectedParts.reduce((sum, item) => sum + item.price, 0));
  1660.         
  1661.         // 内部料金調整を適用: 合計 = パーツ合計 × (1 - 割引率/100) + 追加料金
  1662.         let finalTotal = partsTotal * (1 - discountRate / 100) + additionalCharge;
  1663.         finalTotal = Math.floor(finalTotal); // 小数点以下切り捨て
  1664.         console.log('カート追加:', {
  1665.             partsTotal: partsTotal,
  1666.             discountRate: discountRate,
  1667.             additionalCharge: additionalCharge,
  1668.             finalTotal: finalTotal,
  1669.             selectedParts: selectedParts
  1670.         });
  1671.         // ボタンを無効化
  1672.         \$(this).prop('disabled', true).text('商品作成中...');
  1673.         // カートに追加リクエスト
  1674.         const ajaxUrl = '/estimate_simulator/add_to_cart';
  1675.         console.log('AJAX URL:', ajaxUrl);
  1676.         
  1677.         \$.ajax({
  1678.             url: ajaxUrl,
  1679.             type: 'POST',
  1680.             contentType: 'application/json',
  1681.             data: JSON.stringify({
  1682.                 selected_parts: selectedParts,
  1683.                 total_price: finalTotal
  1684.             }),
  1685.             beforeSend: function() {
  1686.                 console.log('AJAXリクエスト送信開始');
  1687.             },
  1688.             success: function(response) {
  1689.                 console.log('AJAX成功:', response);
  1690.                 if (response.success) {
  1691.                     console.log('リダイレクト開始:', response.product_url);
  1692.                     
  1693.                     // 複数の方法でリダイレクトを試行
  1694.                     try {
  1695.                         window.location.href = response.product_url;
  1696.                     } catch(e) {
  1697.                         console.error('location.href失敗:', e);
  1698.                         try {
  1699.                             window.location.assign(response.product_url);
  1700.                         } catch(e2) {
  1701.                             console.error('location.assign失敗:', e2);
  1702.                             window.location.replace(response.product_url);
  1703.                         }
  1704.                     }
  1705.                 } else {
  1706.                     alert(response.message || '商品作成に失敗しました');
  1707.                     \$('#purchaseBtn').prop('disabled', false).text('🛒 すぐに購入');
  1708.                 }
  1709.             },
  1710.             error: function(xhr, status, error) {
  1711.                 console.log('AJAXエラー:', {status: xhr.status, error: error, responseText: xhr.responseText});
  1712.                 let errorMessage = '商品作成中にエラーが発生しました';
  1713.                 if (xhr.responseJSON && xhr.responseJSON.message) {
  1714.                     errorMessage = xhr.responseJSON.message;
  1715.                 }
  1716.                 alert(errorMessage);
  1717.                 \$('#purchaseBtn').prop('disabled', false).text('🛒 すぐに購入');
  1718.             }
  1719.         });
  1720.     });
  1721.     
  1722.     // スマホ用:スクロール検知で固定サマリーを表示/非表示
  1723.     function initStickySummary() {
  1724.         if (window.innerWidth > 768) {
  1725.             return; // タブレット以上は無効
  1726.         }
  1727.         
  1728.         const \$originalSummary = \$('.estimate-summary').first();
  1729.         const \$stickySummary = \$('#stickySummary');
  1730.         
  1731.         if (\$originalSummary.length === 0 || \$stickySummary.length === 0) {
  1732.             return;
  1733.         }
  1734.         
  1735.         function checkSummaryPosition() {
  1736.             const summaryTop = \$originalSummary.offset().top;
  1737.             const summaryBottom = summaryTop + \$originalSummary.outerHeight();
  1738.             const scrollTop = \$(window).scrollTop();
  1739.             const windowBottom = scrollTop + \$(window).height();
  1740.             
  1741.             // 元のサマリーが画面外に出たら固定サマリーを表示
  1742.             if (summaryBottom < scrollTop || summaryTop > windowBottom) {
  1743.                 \$stickySummary.addClass('visible');
  1744.                 \$('body').addClass('sticky-summary-active');
  1745.             } else {
  1746.                 \$stickySummary.removeClass('visible');
  1747.                 \$('body').removeClass('sticky-summary-active');
  1748.             }
  1749.         }
  1750.         
  1751.         \$(window).on('scroll', checkSummaryPosition);
  1752.         \$(window).on('resize', checkSummaryPosition);
  1753.         
  1754.         // 初回チェック(少し遅延させる)
  1755.         setTimeout(checkSummaryPosition, 500);
  1756.     }
  1757.     
  1758.     // DOMが完全に読み込まれた後に初期化
  1759.     initStickySummary();
  1760. });
  1761. </script>
  1762. ";
  1763.         
  1764.         $__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02->leave($__internal_319393461309892924ff6e74d6d6e64287df64b63545b994e100d4ab223aed02_prof);
  1765.         
  1766.         $__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e->leave($__internal_085b0142806202599c7fe3b329164a92397d8978207a37e79d70b8c52599e33e_prof);
  1767.     }
  1768.     public function getTemplateName()
  1769.     {
  1770.         return "@NZEstimateSystem42/default/simulator.twig";
  1771.     }
  1772.     public function isTraitable()
  1773.     {
  1774.         return false;
  1775.     }
  1776.     public function getDebugInfo()
  1777.     {
  1778.         return array (  1710 => 1346,  1474 => 1113,  1470 => 1112,  1462 => 1106,  1456 => 1103,  1453 => 1102,  1451 => 1101,  1427 => 1079,  1421 => 1076,  1418 => 1075,  1416 => 1074,  1371 => 1031,  1365 => 1028,  1362 => 1027,  1360 => 1026,  1317 => 985,  1303 => 984,  1299 => 983,  1296 => 982,  1282 => 981,  1278 => 980,  1274 => 979,  1270 => 978,  1266 => 977,  1262 => 976,  1258 => 975,  1254 => 974,  1251 => 973,  1234 => 972,  1229 => 970,  1225 => 969,  1221 => 968,  1218 => 967,  1201 => 966,  1197 => 964,  1192 => 960,  1178 => 959,  1164 => 958,  1160 => 957,  1156 => 956,  1152 => 955,  1148 => 954,  1144 => 953,  1140 => 952,  1136 => 951,  1132 => 950,  1128 => 949,  1125 => 948,  1107 => 947,  1090 => 946,  1086 => 944,  1083 => 942,  1074 => 938,  1065 => 935,  1061 => 934,  1057 => 933,  1053 => 932,  1049 => 931,  1044 => 930,  1040 => 929,  1035 => 927,  1031 => 926,  1027 => 925,  1024 => 924,  1020 => 921,  1016 => 919,  1012 => 917,  1010 => 916,  1006 => 915,  999 => 912,  995 => 911,  989 => 907,  985 => 905,  979 => 903,  977 => 902,  973 => 900,  969 => 898,  963 => 896,  961 => 895,  68 => 4,  58 => 3,  35 => 1,);
  1779.     }
  1780.     public function getSourceContext()
  1781.     {
  1782.         return new Source("{% extends 'default_frame.twig' %}
  1783. {% block main %}
  1784. <style>
  1785. /* フォント強制読み込み */
  1786. @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap');
  1787. * {
  1788.     font-family: -apple-system, BlinkMacSystemFont, \"Helvetica Neue\", \"Segoe UI\", Arial, \"Hiragino Kaku Gothic ProN\", \"Hiragino Sans\", Meiryo, \"Noto Sans JP\", sans-serif !important;
  1789. }
  1790. /* 見積もりシミュレーター専用スタイル */
  1791. .nz-estimate-wrapper {
  1792.     max-width: 1200px;
  1793.     margin: 60px auto;
  1794.     padding: 0 20px;
  1795. }
  1796. .nz-estimate-card {
  1797.     background: #ffffff;
  1798.     border-radius: 20px;
  1799.     box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
  1800.     overflow: hidden;
  1801. }
  1802. .nz-estimate-header {
  1803.     background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  1804.     padding: 60px 40px;
  1805.     text-align: center;
  1806. }
  1807. .nz-estimate-title {
  1808.     font-size: 42px;
  1809.     font-weight: 800;
  1810.     color: #ffffff;
  1811.     margin: 0 0 16px 0;
  1812.     letter-spacing: -0.5px;
  1813. }
  1814. .nz-estimate-description {
  1815.     font-size: 18px;
  1816.     color: rgba(255, 255, 255, 0.95);
  1817.     line-height: 1.7;
  1818. }
  1819. .nz-estimate-description img {
  1820.     max-width: 100%;
  1821.     height: auto;
  1822.     display: block;
  1823.     margin: 16px auto;
  1824.     border-radius: 8px;
  1825. }
  1826. .nz-estimate-description p {
  1827.     margin: 8px 0;
  1828. }
  1829. .nz-estimate-description br {
  1830.     display: block;
  1831.     content: \"\";
  1832.     margin: 4px 0;
  1833. }
  1834. .nz-estimate-body {
  1835.     padding: 50px 40px;
  1836. }
  1837. /* カテゴリセクション */
  1838. .category-section {
  1839.     margin-bottom: 20px;
  1840.     overflow: hidden;
  1841. }
  1842. .category-row {
  1843.     display: flex;
  1844.     align-items: center;
  1845.     gap: 20px;
  1846.     width: 100%;
  1847.     max-width: 100%;
  1848. }
  1849. .category-section.child-category {
  1850.     margin-left: 40px;
  1851.     padding-left: 20px;
  1852.     border-left: 3px solid #cbd5e0;
  1853.     display: none;
  1854. }
  1855. .category-section.child-category.active {
  1856.     display: block;
  1857.     animation: slideDown 0.3s ease;
  1858. }
  1859. @keyframes slideDown {
  1860.     from { opacity: 0; transform: translateY(-10px); }
  1861.     to { opacity: 1; transform: translateY(0); }
  1862. }
  1863. .category-header {
  1864.     display: flex;
  1865.     align-items: center;
  1866.     gap: 15px;
  1867.     margin-bottom: 0;
  1868. }
  1869. .category-title {
  1870.     font-size: 20px;
  1871.     font-weight: 700;
  1872.     color: #2d3748;
  1873.     margin: 0;
  1874.     min-width: 150px;
  1875.     flex-shrink: 0;
  1876. }
  1877. .category-title.child {
  1878.     font-size: 18px;
  1879.     color: #4a5568;
  1880. }
  1881. .category-required-badge {
  1882.     display: inline-block;
  1883.     background: #e53e3e;
  1884.     color: white;
  1885.     font-size: 12px;
  1886.     font-weight: 700;
  1887.     padding: 4px 12px;
  1888.     border-radius: 12px;
  1889. }
  1890. .category-optional-badge {
  1891.     display: inline-block;
  1892.     background: #718096;
  1893.     color: white;
  1894.     font-size: 12px;
  1895.     font-weight: 700;
  1896.     padding: 4px 12px;
  1897.     border-radius: 12px;
  1898. }
  1899. .parts-select {
  1900.     flex: 1;
  1901.     width: 100%;
  1902.     max-width: 100%;
  1903.     padding: 12px 16px;
  1904.     font-size: 16px;
  1905.     color: #2d3748 !important;
  1906.     background-color: #f7fafc !important;
  1907.     border: 2px solid #e2e8f0 !important;
  1908.     border-radius: 12px !important;
  1909.     transition: all 0.3s ease !important;
  1910.     cursor: pointer;
  1911.     box-sizing: border-box;
  1912. }
  1913. .parts-select:focus {
  1914.     outline: none !important;
  1915.     border-color: #667eea !important;
  1916.     background-color: #ffffff !important;
  1917.     box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
  1918. }
  1919. .parts-select option {
  1920.     padding: 10px;
  1921. }
  1922. .parts-select option[value=\"\"] {
  1923.     color: #a0aec0 !important;
  1924.     font-style: italic;
  1925. }
  1926. .parts-select option:disabled {
  1927.     color: #a0aec0 !important;
  1928. }
  1929. /* 見積もりサマリー(常時表示) */
  1930. .estimate-summary {
  1931.     background: linear-gradient(135deg, #f7fafc 0%, #edf2f7 100%);
  1932.     border-radius: 16px;
  1933.     padding: 30px;
  1934.     margin-top: 40px;
  1935. }
  1936. .summary-header {
  1937.     margin-bottom: 20px;
  1938.     padding-bottom: 15px;
  1939. }
  1940. .summary-note {
  1941.     font-size: 14px;
  1942.     color: #1a202c;
  1943.     font-weight: 600;
  1944.     text-align: center;
  1945. }
  1946. .summary-items {
  1947.     margin-bottom: 20px;
  1948.     min-height: 100px;
  1949. }
  1950. .summary-empty {
  1951.     text-align: center;
  1952.     padding: 40px 20px;
  1953.     color: #a0aec0;
  1954.     font-size: 15px;
  1955. }
  1956. .summary-item {
  1957.     display: block;
  1958.     padding: 12px 0;
  1959.     border-bottom: 1px solid #e2e8f0;
  1960. }
  1961. .summary-item-left {
  1962.     width: 100%;
  1963. }
  1964. .summary-item-category {
  1965.     font-size: 13px;
  1966.     color: #718096;
  1967.     margin-bottom: 4px;
  1968. }
  1969. .summary-item-name {
  1970.     font-size: 15px;
  1971.     font-weight: 600;
  1972.     color: #2d3748;
  1973. }
  1974. .summary-total {
  1975.     display: flex;
  1976.     justify-content: space-between;
  1977.     align-items: center;
  1978.     padding: 24px 28px;
  1979.     margin-top: 20px;
  1980.     border-top: none;
  1981.     background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  1982.     border-radius: 12px;
  1983.     box-shadow: 0 4px 12px rgba(168, 179, 255, 0.3);
  1984. }
  1985. .summary-total-label {
  1986.     font-size: 22px;
  1987.     font-weight: 700;
  1988.     color: #ffffff;
  1989. }
  1990. .summary-total-price {
  1991.     font-size: 36px;
  1992.     font-weight: 800;
  1993.     color: #ffffff;
  1994.     text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  1995. }
  1996. .summary-total-price.hidden {
  1997.     color: rgba(255, 255, 255, 0.4);
  1998.     text-shadow: none;
  1999. }
  2000. /* 必須項目未選択警告 */
  2001. .required-warning {
  2002.     background: #fff5f5;
  2003.     border: 2px solid #fc8181;
  2004.     border-radius: 12px;
  2005.     padding: 15px 20px;
  2006.     margin-top: 20px;
  2007.     text-align: center;
  2008.     display: none;
  2009. }
  2010. .required-warning.active {
  2011.     display: block;
  2012. }
  2013. .required-warning-text {
  2014.     font-size: 14px;
  2015.     font-weight: 600;
  2016.     color: #742a2a;
  2017. }
  2018. /* お客様情報フォーム */
  2019. .customer-form {
  2020.     margin-top: 30px;
  2021.     padding: 25px;
  2022.     background: #f7fafc;
  2023.     border-radius: 12px;
  2024.     border: 1px solid #e2e8f0;
  2025.     display: none;
  2026. }
  2027. .customer-form.active {
  2028.     display: block;
  2029.     animation: fadeIn 0.3s ease;
  2030. }
  2031. @keyframes fadeIn {
  2032.     from { opacity: 0; transform: translateY(10px); }
  2033.     to { opacity: 1; transform: translateY(0); }
  2034. }
  2035. .customer-form h3 {
  2036.     font-size: 20px;
  2037.     font-weight: 700;
  2038.     color: #2d3748;
  2039.     margin-bottom: 20px;
  2040. }
  2041. .form-group {
  2042.     margin-bottom: 25px;
  2043. }
  2044. .form-row {
  2045.     display: flex;
  2046.     gap: 20px;
  2047.     margin-bottom: 0;
  2048. }
  2049. .form-group-half {
  2050.     flex: 1;
  2051.     margin-bottom: 25px;
  2052. }
  2053. .form-label {
  2054.     display: block;
  2055.     font-size: 16px;
  2056.     font-weight: 700;
  2057.     color: #2d3748;
  2058.     margin-bottom: 10px;
  2059. }
  2060. .form-label .required {
  2061.     color: #e53e3e;
  2062.     margin-left: 4px;
  2063. }
  2064. .form-control {
  2065.     width: 100%;
  2066.     padding: 14px 18px;
  2067.     font-size: 16px;
  2068.     color: #2d3748 !important;
  2069.     background-color: #f7fafc !important;
  2070.     border: 2px solid #e2e8f0 !important;
  2071.     border-radius: 12px !important;
  2072.     transition: all 0.3s ease !important;
  2073. }
  2074. .form-control:focus {
  2075.     outline: none !important;
  2076.     border-color: #667eea !important;
  2077.     background-color: #ffffff !important;
  2078.     box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
  2079. }
  2080. textarea.form-control {
  2081.     min-height: 120px;
  2082.     resize: vertical;
  2083. }
  2084. /* 送信ボタン */
  2085. .submit-section {
  2086.     margin-top: 40px;
  2087.     text-align: center;
  2088.     display: none;
  2089. }
  2090. .submit-section.active {
  2091.     display: block;
  2092.     animation: fadeIn 0.3s ease;
  2093. }
  2094. .submit-btn {
  2095.     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  2096.     color: #ffffff;
  2097.     font-size: 18px;
  2098.     font-weight: 700;
  2099.     padding: 18px 60px;
  2100.     border: none;
  2101.     border-radius: 50px;
  2102.     cursor: pointer;
  2103.     transition: all 0.3s ease;
  2104.     box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
  2105. }
  2106. .submit-btn:hover {
  2107.     transform: translateY(-2px);
  2108.     box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
  2109. }
  2110. .submit-btn:disabled {
  2111.     background: #cbd5e0;
  2112.     cursor: not-allowed;
  2113.     box-shadow: none;
  2114. }
  2115. /* ローディング */
  2116. .loading {
  2117.     text-align: center;
  2118.     padding: 40px;
  2119.     display: none;
  2120. }
  2121. .loading.active {
  2122.     display: block;
  2123. }
  2124. .loading-spinner {
  2125.     border: 4px solid #e2e8f0;
  2126.     border-top: 4px solid #667eea;
  2127.     border-radius: 50%;
  2128.     width: 50px;
  2129.     height: 50px;
  2130.     animation: spin 1s linear infinite;
  2131.     margin: 0 auto 20px;
  2132. }
  2133. @keyframes spin {
  2134.     0% { transform: rotate(0deg); }
  2135.     100% { transform: rotate(360deg); }
  2136. }
  2137. /* 完了メッセージ */
  2138. .complete-message {
  2139.     text-align: center;
  2140.     padding: 60px 40px;
  2141.     display: none;
  2142. }
  2143. .complete-message.active {
  2144.     display: block;
  2145.     animation: fadeIn 0.5s ease;
  2146. }
  2147. .complete-icon {
  2148.     font-size: 72px;
  2149.     color: #48bb78;
  2150.     margin-bottom: 20px;
  2151. }
  2152. .complete-title {
  2153.     font-size: 28px;
  2154.     font-weight: 700;
  2155.     color: #2d3748;
  2156.     margin-bottom: 15px;
  2157. }
  2158. .complete-text {
  2159.     font-size: 16px;
  2160.     color: #718096;
  2161.     line-height: 1.8;
  2162. }
  2163. /* 注意事項 */
  2164. .notice-text {
  2165.     margin-top: 20px;
  2166.     padding: 15px 20px;
  2167.     background-color: #fffbeb;
  2168.     border-left: 4px solid #f59e0b;
  2169.     border-radius: 4px;
  2170.     color: #92400e;
  2171.     font-size: 14px;
  2172.     line-height: 1.7;
  2173. }
  2174. /* フリーエリア */
  2175. .nz-free-area {
  2176.     max-width: 1200px;
  2177.     margin: 40px auto;
  2178.     padding: 0 20px;
  2179.     text-align: center;
  2180. }
  2181. .nz-free-area h2 {
  2182.     font-size: 28px;
  2183.     font-weight: 700;
  2184.     color: #2d3748;
  2185.     margin-bottom: 20px;
  2186.     text-align: center;
  2187. }
  2188. .nz-free-area h3 {
  2189.     font-size: 22px;
  2190.     font-weight: 600;
  2191.     color: #4a5568;
  2192.     margin: 30px 0 15px;
  2193.     text-align: left;
  2194. }
  2195. .nz-free-area p {
  2196.     font-size: 16px;
  2197.     color: #718096;
  2198.     line-height: 1.8;
  2199.     margin-bottom: 15px;
  2200.     text-align: left;
  2201. }
  2202. .nz-free-area img {
  2203.     max-width: 100%;
  2204.     height: auto;
  2205.     border-radius: 8px;
  2206.     margin: 20px 0;
  2207. }
  2208. .nz-free-area .btn {
  2209.     display: inline-block;
  2210.     width: 100%;
  2211.     max-width: 400px;
  2212.     padding: 18px 30px;
  2213.     background: linear-gradient(135deg, #10b981 0%, #059669 100%);
  2214.     color: white !important;
  2215.     text-decoration: none;
  2216.     border-radius: 16px;
  2217.     font-weight: 700;
  2218.     font-size: 18px;
  2219.     text-align: center;
  2220.     transition: all 0.3s ease;
  2221.     border: none;
  2222.     cursor: pointer;
  2223.     box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
  2224. }
  2225. .nz-free-area .btn:hover {
  2226.     background: linear-gradient(135deg, #059669 0%, #047857 100%);
  2227.     transform: translateY(-2px);
  2228.     box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
  2229. }
  2230. /* 購入セクション */
  2231. .purchase-section {
  2232.     margin-bottom: 40px;
  2233. }
  2234. .purchase-card {
  2235.     background: #ffffff;
  2236.     border: none;
  2237.     border-radius: 0;
  2238.     padding: 20px 0;
  2239.     text-align: center;
  2240. }
  2241. .purchase-btn {
  2242.     width: 100%;
  2243.     max-width: 400px;
  2244.     padding: 18px;
  2245.     background: linear-gradient(135deg, #10b981 0%, #059669 100%);
  2246.     color: white;
  2247.     border: none;
  2248.     border-radius: 12px;
  2249.     font-size: 18px;
  2250.     font-weight: 700;
  2251.     cursor: pointer;
  2252.     transition: all 0.3s ease;
  2253.     box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
  2254.     margin-bottom: 15px;
  2255. }
  2256. .purchase-btn:hover {
  2257.     transform: translateY(-2px);
  2258.     box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
  2259. }
  2260. .purchase-btn:active {
  2261.     transform: translateY(0);
  2262. }
  2263. .purchase-notice {
  2264.     margin-top: 15px;
  2265.     padding: 15px;
  2266.     background-color: #f0fdf4;
  2267.     border-radius: 8px;
  2268.     color: #065f46;
  2269.     font-size: 14px;
  2270.     line-height: 1.7;
  2271. }
  2272. /* スマホ用固定サマリー:PC/タブレットでは非表示 */
  2273. .estimate-summary-sticky {
  2274.     display: none;
  2275. }
  2276. /* ==================== レスポンシブ対応 ==================== */
  2277. @media (max-width: 768px) {
  2278.     /* コンテナ */
  2279.     .estimate-container {
  2280.         padding: 10px;
  2281.     }
  2282.     
  2283.     /* ヘッダー */
  2284.     .estimate-header {
  2285.         padding: 20px 10px;
  2286.         margin-bottom: 15px;
  2287.     }
  2288.     
  2289.     .estimate-header h1 {
  2290.         font-size: 20px;
  2291.     }
  2292.     
  2293.     .estimate-header p {
  2294.         font-size: 13px;
  2295.     }
  2296.     
  2297.     /* カテゴリセクション */
  2298.     .category-section {
  2299.         padding: 15px;
  2300.         margin-bottom: 12px;
  2301.         width: 100%;
  2302.         box-sizing: border-box;
  2303.     }
  2304.     
  2305.     .category-row {
  2306.         display: flex;
  2307.         flex-direction: column;
  2308.         gap: 10px;
  2309.         width: 100%;
  2310.         max-width: 100%;
  2311.         box-sizing: border-box;
  2312.     }
  2313.     
  2314.     .category-header {
  2315.         flex-direction: row;
  2316.         align-items: center;
  2317.         gap: 10px;
  2318.         margin-bottom: 0;
  2319.         width: 100%;
  2320.     }
  2321.     
  2322.     .category-name {
  2323.         font-size: 16px;
  2324.     }
  2325.     
  2326.     .required-badge {
  2327.         font-size: 11px;
  2328.         padding: 3px 8px;
  2329.     }
  2330.     
  2331.     /* セレクトボックス */
  2332.     .parts-select {
  2333.         font-size: 15px;
  2334.         padding: 12px 35px 12px 12px;
  2335.         margin-top: 0;
  2336.         width: 100% !important;
  2337.         max-width: 100% !important;
  2338.         flex: none;
  2339.     }
  2340.     
  2341.     /* 見積もりサマリー */
  2342.     .estimate-summary {
  2343.         padding: 15px;
  2344.         margin-bottom: 15px;
  2345.     }
  2346.     
  2347.     .summary-note {
  2348.         font-size: 12px;
  2349.     }
  2350.     
  2351.     .summary-total {
  2352.         flex-direction: row;
  2353.         align-items: center;
  2354.         gap: 15px;
  2355.         padding: 12px 0;
  2356.     }
  2357.     
  2358.     .summary-total-label {
  2359.         font-size: 15px;
  2360.     }
  2361.     
  2362.     .summary-total-price {
  2363.         font-size: 24px;
  2364.         text-align: right;
  2365.     }
  2366.     
  2367.     /* 購入セクション */
  2368.     .purchase-section {
  2369.         margin-bottom: 15px;
  2370.     }
  2371.     
  2372.     .purchase-card {
  2373.         padding: 15px;
  2374.     }
  2375.     
  2376.     .purchase-btn {
  2377.         max-width: 100%;
  2378.         font-size: 15px;
  2379.         padding: 14px;
  2380.     }
  2381.     
  2382.     .purchase-notice {
  2383.         font-size: 12px;
  2384.         padding: 10px;
  2385.         margin-top: 10px;
  2386.     }
  2387.     
  2388.     /* お客様情報フォーム */
  2389.     .customer-form {
  2390.         padding: 15px;
  2391.         margin-top: 15px;
  2392.     }
  2393.     
  2394.     .customer-form h3 {
  2395.         font-size: 16px;
  2396.         margin-bottom: 12px;
  2397.     }
  2398.     
  2399.     .form-row {
  2400.         flex-direction: column;
  2401.         gap: 0;
  2402.     }
  2403.     
  2404.     .form-group {
  2405.         margin-bottom: 15px;
  2406.     }
  2407.     
  2408.     .form-label {
  2409.         font-size: 14px;
  2410.         margin-bottom: 6px;
  2411.     }
  2412.     
  2413.     .form-control {
  2414.         padding: 10px 12px;
  2415.         font-size: 15px;
  2416.     }
  2417.     
  2418.     textarea.form-control {
  2419.         min-height: 80px;
  2420.     }
  2421.     
  2422.     /* 送信ボタン */
  2423.     .submit-section {
  2424.         padding: 15px;
  2425.         margin-top: 15px;
  2426.     }
  2427.     
  2428.     .submit-btn {
  2429.         padding: 14px;
  2430.         font-size: 15px;
  2431.     }
  2432.     
  2433.     /* 必須警告 */
  2434.     .required-warning {
  2435.         padding: 10px;
  2436.         font-size: 12px;
  2437.         margin: 10px 0;
  2438.     }
  2439.     
  2440.     /* フリーエリア */
  2441.     .nz-free-area {
  2442.         padding: 20px 15px;
  2443.         font-size: 14px;
  2444.     }
  2445.     
  2446.     .nz-free-area h2 {
  2447.         font-size: 20px;
  2448.     }
  2449.     
  2450.     .nz-free-area h3 {
  2451.         font-size: 18px;
  2452.     }
  2453. }
  2454. @media (max-width: 480px) {
  2455.     /* さらに小さい画面用 */
  2456.     .estimate-container {
  2457.         padding: 8px;
  2458.     }
  2459.     
  2460.     .estimate-header {
  2461.         padding: 15px 8px;
  2462.     }
  2463.     
  2464.     .estimate-header h1 {
  2465.         font-size: 18px;
  2466.     }
  2467.     
  2468.     .estimate-header p {
  2469.         font-size: 12px;
  2470.     }
  2471.     
  2472.     .category-section {
  2473.         padding: 12px;
  2474.         margin-bottom: 10px;
  2475.     }
  2476.     
  2477.     .category-row {
  2478.         display: flex;
  2479.         flex-direction: column;
  2480.         gap: 8px;
  2481.     }
  2482.     
  2483.     .category-name {
  2484.         font-size: 15px;
  2485.     }
  2486.     
  2487.     .parts-select {
  2488.         font-size: 14px;
  2489.         padding: 10px 30px 10px 10px;
  2490.         width: 100% !important;
  2491.         max-width: 100% !important;
  2492.         flex: none;
  2493.     }
  2494.     
  2495.     .estimate-summary {
  2496.         padding: 12px;
  2497.     }
  2498.     
  2499.     .summary-title {
  2500.         font-size: 15px;
  2501.     }
  2502.     
  2503.     .summary-total {
  2504.         padding: 16px 20px !important;
  2505.         border-radius: 12px;
  2506.     }
  2507.     
  2508.     .summary-total-label {
  2509.         font-size: 18px !important;
  2510.     }
  2511.     
  2512.     .summary-total-price {
  2513.         font-size: 26px !important;
  2514.     }
  2515.     
  2516.     /* スマホ用:下部固定サマリー(クローン) */
  2517.     .estimate-summary-sticky {
  2518.         display: none; /* 初期状態では非表示 */
  2519.         position: fixed;
  2520.         bottom: 0;
  2521.         left: 0;
  2522.         right: 0;
  2523.         background: linear-gradient(135deg, #a8b3ff 0%, #d4a5ff 100%);
  2524.         box-shadow: 0 -4px 12px rgba(168, 179, 255, 0.4);
  2525.         z-index: 9999;
  2526.         padding: 8px 16px;
  2527.     }
  2528.     
  2529.     .estimate-summary-sticky.visible {
  2530.         display: block !important; /* スクロールで表示 */
  2531.     }
  2532.     
  2533.     .estimate-summary-sticky .summary-total {
  2534.         margin: 0;
  2535.         padding: 0;
  2536.         border-top: none;
  2537.         display: flex;
  2538.         justify-content: space-between;
  2539.         align-items: center;
  2540.         background: transparent;
  2541.         box-shadow: none;
  2542.         border-radius: 0;
  2543.     }
  2544.     
  2545.     .estimate-summary-sticky .summary-total-label {
  2546.         font-size: 14px;
  2547.         font-weight: 700;
  2548.         color: #ffffff;
  2549.     }
  2550.     
  2551.     .estimate-summary-sticky .summary-total-price {
  2552.         font-size: 20px;
  2553.         font-weight: 800;
  2554.         color: #ffffff;
  2555.         text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  2556.     }
  2557.     
  2558.     /* コンテンツが固定サマリーに隠れないよう余白 */
  2559.     body.sticky-summary-active {
  2560.         padding-bottom: 50px;
  2561.     }
  2562.     
  2563.     .purchase-card {
  2564.         padding: 12px;
  2565.     }
  2566.     
  2567.     .purchase-btn {
  2568.         font-size: 14px;
  2569.         padding: 12px;
  2570.     }
  2571.     
  2572.     .customer-form {
  2573.         padding: 12px;
  2574.     }
  2575.     
  2576.     .form-group {
  2577.         margin-bottom: 12px;
  2578.     }
  2579.     
  2580.     .submit-section {
  2581.         padding: 12px;
  2582.     }
  2583. }
  2584. </style>
  2585. <div class=\"nz-estimate-wrapper\">
  2586.     <div class=\"nz-estimate-card\">
  2587.         <div class=\"nz-estimate-header\">
  2588.             <h1 class=\"nz-estimate-title\">
  2589.                 {% if config.pageTitle %}
  2590.                     {{ config.pageTitle|replace({'&lt;': '<', '&gt;': '>', '&quot;': '\"', '&#039;': \"'\", '&amp;': '&'})|raw }}
  2591.                 {% else %}
  2592.                     見積もりシミュレーター
  2593.                 {% endif %}
  2594.             </h1>
  2595.             <div class=\"nz-estimate-description\">
  2596.                 {% if config.pageDescription %}
  2597.                     {{ config.pageDescription|replace({'&lt;': '<', '&gt;': '>', '&quot;': '\"', '&#039;': \"'\", '&amp;': '&'})|raw }}
  2598.                 {% else %}
  2599.                     パーツを選択して、お見積もりを作成いただけます。
  2600.                 {% endif %}
  2601.             </div>
  2602.         </div>
  2603.         <div class=\"nz-estimate-body\">
  2604.             {% for item in categoriesWithParts %}
  2605.                 <div class=\"category-section\" data-category-id=\"{{ item.category.id }}\">
  2606.                     <div class=\"category-row\">
  2607.                         <div class=\"category-header\">
  2608.                             <h2 class=\"category-title\">{{ item.category.name }}</h2>
  2609.                             {% if item.category.isRequired %}
  2610.                                 <span class=\"category-required-badge\">必須</span>
  2611.                             {% else %}
  2612.                                 <span class=\"category-optional-badge\">任意</span>
  2613.                             {% endif %}
  2614.                         </div>
  2615.                         
  2616.                         {# パーツを選択 #}
  2617.                         <select class=\"parts-select\" 
  2618.                                 data-category-id=\"{{ item.category.id }}\" 
  2619.                                 data-required=\"{{ item.category.isRequired ? '1' : '0' }}\"
  2620.                                 data-category-name=\"{{ item.category.name }}\">
  2621.                             <option value=\"\">選択してください</option>
  2622.                             {% for parts in item.parts %}
  2623.                                 <option value=\"{{ parts.id }}\" 
  2624.                                         data-price=\"{{ parts.price }}\"
  2625.                                         data-name=\"{{ parts.productName }}\"
  2626.                                         data-child-category-id=\"{{ parts.childCategoryId ? parts.childCategoryId : '' }}\"
  2627.                                         data-child-filter-keywords=\"{{ parts.childFilterKeywords ? parts.childFilterKeywords : '' }}\">
  2628.                                     {{ parts.productName }}
  2629.                                 </option>
  2630.                             {% endfor %}
  2631.                         </select>
  2632.                     </div>
  2633.                 </div>
  2634.             {% endfor %}
  2635.             
  2636.             {# 全パーツデータをJavaScript用に埋め込み #}
  2637.             <script type=\"application/json\" id=\"allPartsData\">
  2638.             [
  2639.                 {% for item in categoriesWithParts %}
  2640.                     {% for parts in item.parts %}
  2641.                     {
  2642.                         \"id\": {{ parts.id }},
  2643.                         \"category_id\": {{ item.category.id }},
  2644.                         \"category_name\": {{ item.category.name|json_encode|raw }},
  2645.                         \"name\": {{ parts.productName|json_encode|raw }},
  2646.                         \"model_number\": {{ parts.modelNumber|default('')|json_encode|raw }},
  2647.                         \"price\": {{ parts.price }},
  2648.                         \"child_category_id\": {{ parts.childCategoryId ? parts.childCategoryId|json_encode|raw : 'null' }},
  2649.                         \"child_filter_keywords\": {{ parts.childFilterKeywords ? parts.childFilterKeywords|json_encode|raw : 'null' }}
  2650.                     }{% if not loop.parent.loop.last or not loop.last %},{% endif %}
  2651.                     {% endfor %}
  2652.                 {% endfor %}
  2653.             ]
  2654.             </script>
  2655.             
  2656.             {# カテゴリデータをJavaScriptに埋め込み(子カテゴリを含むすべて) #}
  2657.             <script type=\"application/json\" id=\"allCategoriesData\">
  2658.             [
  2659.                 {% for item in allCategoriesWithParts %}
  2660.                 {
  2661.                     \"id\": {{ item.category.id }},
  2662.                     \"name\": {{ item.category.name|json_encode|raw }},
  2663.                     \"is_required\": {{ item.category.isRequired ? 'true' : 'false' }},
  2664.                     \"parts\": [
  2665.                         {% for parts in item.parts %}
  2666.                         {
  2667.                             \"id\": {{ parts.id }},
  2668.                             \"name\": {{ parts.productName|json_encode|raw }},
  2669.                             \"model_number\": {{ parts.modelNumber|default('')|json_encode|raw }},
  2670.                             \"price\": {{ parts.price }},
  2671.                             \"child_category_id\": {{ parts.childCategoryId ? parts.childCategoryId|json_encode|raw : 'null' }},
  2672.                             \"child_filter_keywords\": {{ parts.childFilterKeywords ? parts.childFilterKeywords|json_encode|raw : 'null' }}
  2673.                         }{% if not loop.last %},{% endif %}
  2674.                         {% endfor %}
  2675.                     ]
  2676.                 }{% if not loop.last %},{% endif %}
  2677.                 {% endfor %}
  2678.             ]
  2679.             </script>
  2680.             <!-- 見積もりサマリー(常時表示) -->
  2681.             <div class=\"estimate-summary\">
  2682.                 <div class=\"summary-header\">
  2683.                     <div class=\"summary-note\">※ 必須項目をすべて選択すると金額が表示されます</div>
  2684.                 </div>
  2685.                 
  2686.                 <!-- 必須項目未選択警告 -->
  2687.                 <div class=\"required-warning\" id=\"requiredWarning\">
  2688.                     <div class=\"required-warning-text\">
  2689.                         ⚠️ 必須カテゴリをすべて選択してください
  2690.                     </div>
  2691.                 </div>
  2692.                 
  2693.                 <div class=\"summary-total\">
  2694.                     <div class=\"summary-total-label\">合計金額</div>
  2695.                     <div class=\"summary-total-price\" id=\"totalPriceArea\">
  2696.                         ¥<span id=\"totalPrice\">---</span>
  2697.                     </div>
  2698.                 </div>
  2699.             </div>
  2700.             <!-- スマホ用:下部固定サマリー -->
  2701.             <div class=\"estimate-summary-sticky\" id=\"stickySummary\">
  2702.                 <div class=\"summary-total\">
  2703.                     <div class=\"summary-total-label\">合計金額</div>
  2704.                     <div class=\"summary-total-price\">
  2705.                         ¥<span id=\"stickyTotalPrice\">---</span>
  2706.                     </div>
  2707.                 </div>
  2708.             </div>
  2709.             <!-- 今すぐ購入セクション -->
  2710.             <div class=\"purchase-section\" id=\"purchaseSection\" style=\"display: none;\">
  2711.                 <div class=\"purchase-card\">
  2712.                     <button type=\"button\" class=\"purchase-btn\" id=\"purchaseBtn\">
  2713.                         🛒 すぐに購入
  2714.                     </button>
  2715.                     
  2716.                     {% if config.purchaseNoticeText %}
  2717.                     <div class=\"purchase-notice\">
  2718.                         {{ config.purchaseNoticeText|nl2br }}
  2719.                     </div>
  2720.                     {% endif %}
  2721.                 </div>
  2722.             </div>
  2723.             <!-- お客様情報フォーム -->
  2724.             <div class=\"customer-form\" id=\"customerForm\">
  2725.                 <h3>お客様情報</h3>
  2726.                 <form id=\"estimateForm\">
  2727.                     <div class=\"form-row\">
  2728.                         <div class=\"form-group form-group-half\">
  2729.                             <label class=\"form-label\">
  2730.                                 お名前<span class=\"required\">*</span>
  2731.                             </label>
  2732.                             <input type=\"text\" name=\"name\" class=\"form-control\" required>
  2733.                         </div>
  2734.                         
  2735.                         <div class=\"form-group form-group-half\">
  2736.                             <label class=\"form-label\">
  2737.                                 電話番号<span class=\"required\">*</span>
  2738.                             </label>
  2739.                             <input type=\"tel\" name=\"tel\" class=\"form-control\" required>
  2740.                         </div>
  2741.                     </div>
  2742.                     
  2743.                     <div class=\"form-group\">
  2744.                         <label class=\"form-label\">
  2745.                             メールアドレス<span class=\"required\">*</span>
  2746.                         </label>
  2747.                         <input type=\"email\" name=\"email\" class=\"form-control\" required>
  2748.                     </div>
  2749.                     
  2750.                     <div class=\"form-group\">
  2751.                         <label class=\"form-label\">ご要望・備考</label>
  2752.                         <textarea name=\"message\" class=\"form-control\" rows=\"3\"></textarea>
  2753.                     </div>
  2754.                 </form>
  2755.             </div>
  2756.             <!-- 送信ボタン -->
  2757.             <div class=\"submit-section\" id=\"submitSection\">
  2758.                 <button type=\"button\" class=\"submit-btn\" id=\"submitBtn\">
  2759.                     見積もり依頼を送信
  2760.                 </button>
  2761.                 
  2762.                 {% if config.noticeText %}
  2763.                 <div class=\"notice-text\">
  2764.                     {{ config.noticeText|nl2br }}
  2765.                 </div>
  2766.                 {% endif %}
  2767.             </div>
  2768.             <!-- ローディング -->
  2769.             <div class=\"loading\" id=\"loading\">
  2770.                 <div class=\"loading-spinner\"></div>
  2771.                 <p>送信中...</p>
  2772.             </div>
  2773.             <!-- 完了メッセージ -->
  2774.             <div class=\"complete-message\" id=\"completeMessage\">
  2775.                 <div class=\"complete-icon\">✓</div>
  2776.                 <div class=\"complete-title\">送信完了</div>
  2777.                 <div class=\"complete-text\">
  2778.                     お見積もり依頼を受け付けました。<br>
  2779.                     担当者より折り返しご連絡いたします。
  2780.                 </div>
  2781.             </div>
  2782.         </div>
  2783.     </div>
  2784. </div>
  2785. <!-- フリーエリア -->
  2786. {% if config.freeAreaHtml %}
  2787. <div class=\"nz-free-area\">
  2788.     {{ config.freeAreaHtml|raw }}
  2789. </div>
  2790. {% endif %}
  2791. <script>
  2792. \$(function() {
  2793.     let selectedParts = [];
  2794.     
  2795.     // 内部料金設定(顧客には非表示)
  2796.     const additionalCharge = {{ config.additionalCharge|default(0) }};
  2797.     const discountRate = {{ config.discountRate|default(0) }};
  2798.     
  2799.     // データ読み込み
  2800.     const allParts = JSON.parse(\$('#allPartsData').text());
  2801.     const allCategories = JSON.parse(\$('#allCategoriesData').text());
  2802.     
  2803.     // 必須カテゴリを取得
  2804.     const requiredCategories = [];
  2805.     allCategories.forEach(function(cat) {
  2806.         if (cat.is_required) {
  2807.             requiredCategories.push(cat.id);
  2808.         }
  2809.     });
  2810.     
  2811.     // パーツ選択時の処理
  2812.     \$(document).on('change', '.parts-select', function() {
  2813.         const \$select = \$(this);
  2814.         const partsId = \$select.val();
  2815.         const categoryId = \$select.data('category-id');
  2816.         const \$currentSection = \$select.closest('.category-section');
  2817.         
  2818.         // 現在のセクションの後続の子カテゴリを削除(アニメーション付き)
  2819.         \$currentSection.nextAll('.category-section.child-category').each(function() {
  2820.             const \$childSection = \$(this);
  2821.             const parentId = \$childSection.data('parent-id');
  2822.             
  2823.             // 現在のカテゴリの子カテゴリのみ削除
  2824.             if (parentId == categoryId) {
  2825.                 \$childSection.removeClass('active').fadeOut(200, function() {
  2826.                     \$(this).remove();
  2827.                 });
  2828.             }
  2829.         });
  2830.         
  2831.         if (!partsId) {
  2832.             updateSummary();
  2833.             return;
  2834.         }
  2835.         
  2836.         // 選択したパーツ情報を取得
  2837.         const parts = allParts.find(p => p.id == partsId);
  2838.         
  2839.         if (!parts) {
  2840.             updateSummary();
  2841.             return;
  2842.         }
  2843.         
  2844.         // 子カテゴリがあるか確認(カンマ区切りで複数対応)
  2845.         if (parts.child_category_id) {
  2846.             const childCategoryIds = parts.child_category_id.toString().split(',').map(id => parseInt(id.trim())).filter(id => id > 0);
  2847.             
  2848.             console.log('========================================');
  2849.             console.log('選択されたパーツ:', parts.name);
  2850.             console.log('子カテゴリID:', childCategoryIds);
  2851.             console.log('フィルタキーワード:', parts.child_filter_keywords);
  2852.             
  2853.             childCategoryIds.forEach(function(childCatId) {
  2854.                 const childCategory = allCategories.find(c => c.id == childCatId);
  2855.                 
  2856.                 console.log('--- 子カテゴリID:', childCatId, '---');
  2857.                 console.log('子カテゴリ名:', childCategory ? childCategory.name : 'NOT FOUND');
  2858.                 
  2859.                 if (childCategory && childCategory.parts.length > 0) {
  2860.                     let filteredParts = childCategory.parts;
  2861.                     
  2862.                     // フィルタキーワードがある場合は絞り込み
  2863.                     if (parts.child_filter_keywords) {
  2864.                         const keywords = parts.child_filter_keywords.split(',').map(k => k.trim().toLowerCase());
  2865.                         
  2866.                         console.log('=== フィルタ実行 ===');
  2867.                         console.log('子カテゴリ:', childCategory.name);
  2868.                         console.log('フィルタキーワード:', keywords);
  2869.                         console.log('全パーツ数:', childCategory.parts.length);
  2870.                         
  2871.                         filteredParts = childCategory.parts.filter(p => {
  2872.                             const searchText = (p.name + ' ' + (p.model_number || '')).toLowerCase();
  2873.                             const matched = keywords.some(kw => searchText.includes(kw));
  2874.                             console.log(`  - \${p.name}: \${matched ? 'マッチ' : '除外'} (検索文字: \${searchText})`);
  2875.                             return matched;
  2876.                         });
  2877.                         
  2878.                         console.log('フィルタ後パーツ数:', filteredParts.length);
  2879.                     }
  2880.                     
  2881.                     // フィルタ後のパーツがある場合のみ子カテゴリを表示
  2882.                     if (filteredParts.length > 0) {
  2883.                         // 子カテゴリセクションを作成
  2884.                         createChildCategorySection({
  2885.                             ...childCategory,
  2886.                             parts: filteredParts
  2887.                         }, categoryId);
  2888.                     }
  2889.                 }
  2890.             });
  2891.         }
  2892.         
  2893.         updateSummary();
  2894.     });
  2895.     
  2896.     // 子カテゴリセクション作成(セレクトボックス形式)
  2897.     function createChildCategorySection(category, parentCategoryId) {
  2898.         const \$parentSection = \$(`.category-section[data-category-id=\"\${parentCategoryId}\"]`);
  2899.         
  2900.         const childHtml = `
  2901.             <div class=\"category-section child-category\" data-category-id=\"\${category.id}\" data-parent-id=\"\${parentCategoryId}\">
  2902.                 <div class=\"category-row\">
  2903.                     <div class=\"category-header\">
  2904.                         <h2 class=\"category-title child\">└ \${category.name}</h2>
  2905.                         \${category.is_required ? '<span class=\"category-required-badge\">必須</span>' : '<span class=\"category-optional-badge\">任意</span>'}
  2906.                     </div>
  2907.                     
  2908.                     <select class=\"parts-select\" 
  2909.                             data-category-id=\"\${category.id}\" 
  2910.                             data-parent-category-id=\"\${parentCategoryId}\"
  2911.                             data-required=\"\${category.is_required ? '1' : '0'}\"
  2912.                             data-category-name=\"\${category.name}\">
  2913.                         \${category.parts.map((p, index) => `
  2914.                             <option value=\"\${p.id}\" 
  2915.                                     data-price=\"\${p.price}\"
  2916.                                     data-name=\"\${p.name}\"
  2917.                                     data-child-category-id=\"\${p.child_category_id || ''}\"
  2918.                                     data-child-filter-keywords=\"\${p.child_filter_keywords || ''}\"
  2919.                                     \${index === 0 ? 'selected' : ''}>
  2920.                                 \${p.name}
  2921.                             </option>
  2922.                         `).join('')}
  2923.                     </select>
  2924.                 </div>
  2925.             </div>
  2926.         `;
  2927.         
  2928.         \$parentSection.after(childHtml);
  2929.         
  2930.         // 挿入後、activeクラスを追加してスライド表示
  2931.         setTimeout(function() {
  2932.             \$(`.child-category[data-parent-id=\"\${parentCategoryId}\"]`).addClass('active');
  2933.         }, 10);
  2934.     }
  2935.     
  2936.     // サマリー更新
  2937.     function updateSummary() {
  2938.         const items = [];
  2939.         const selectedCategories = new Set();
  2940.         let total = 0;
  2941.         
  2942.         // すべてのセレクトボックスを取得
  2943.         \$('.parts-select').each(function() {
  2944.             const \$select = \$(this);
  2945.             const value = \$select.val();
  2946.             
  2947.             if (value && value !== '') {
  2948.                 const selectedOption = \$select.find('option:selected');
  2949.                 const categoryId = \$select.data('category-id');
  2950.                 const parentCategoryId = \$select.data('parent-category-id');
  2951.                 const categoryName = \$select.data('category-name');
  2952.                 const price = Math.floor(parseFloat(selectedOption.data('price'))); // 小数点以下切り捨て
  2953.                 const name = selectedOption.data('name');
  2954.                 
  2955.                 items.push({
  2956.                     parts_id: value,
  2957.                     category_name: categoryName,
  2958.                     name: name,
  2959.                     price: price
  2960.                 });
  2961.                 
  2962.                 // 必須カテゴリの判定: 子カテゴリの場合は親カテゴリIDを、そうでない場合は自身のIDを使用
  2963.                 const checkCategoryId = parentCategoryId || categoryId;
  2964.                 if (checkCategoryId) {
  2965.                     selectedCategories.add(parseInt(checkCategoryId));
  2966.                 }
  2967.                 total += price;
  2968.             }
  2969.         });
  2970.         
  2971.         selectedParts = items;
  2972.         
  2973.         // 必須カテゴリがすべて選択されているかチェック
  2974.         const allRequiredSelected = requiredCategories.every(catId => selectedCategories.has(catId));
  2975.         
  2976.         if (items.length > 0) {
  2977.             // 合計金額の表示
  2978.             if (allRequiredSelected) {
  2979.                 // 内部料金計算: 合計 = パーツ合計 × (1 - 割引率/100) + 追加料金
  2980.                 let finalTotal = total * (1 - discountRate / 100) + additionalCharge;
  2981.                 finalTotal = Math.round(finalTotal); // 四捨五入
  2982.                 
  2983.                 \$('#totalPrice').text(finalTotal.toLocaleString());
  2984.                 \$('#stickyTotalPrice').text(finalTotal.toLocaleString()); // 固定サマリーも更新
  2985.                 \$('#totalPriceArea').removeClass('hidden');
  2986.                 \$('#requiredWarning').removeClass('active');
  2987.                 \$('#customerForm').addClass('active');
  2988.                 \$('#submitSection').addClass('active');
  2989.                 \$('#purchaseSection').show();
  2990.             } else {
  2991.                 \$('#totalPrice').text('---');
  2992.                 \$('#stickyTotalPrice').text('---'); // 固定サマリーもクリア
  2993.                 \$('#totalPriceArea').addClass('hidden');
  2994.                 \$('#requiredWarning').addClass('active');
  2995.                 \$('#customerForm').removeClass('active');
  2996.                 \$('#submitSection').removeClass('active');
  2997.                 \$('#purchaseSection').hide();
  2998.             }
  2999.         } else {
  3000.             \$('#totalPrice').text('---');
  3001.             \$('#stickyTotalPrice').text('---'); // 固定サマリーもクリア
  3002.             \$('#totalPriceArea').addClass('hidden');
  3003.             \$('#requiredWarning').removeClass('active');
  3004.             \$('#customerForm').removeClass('active');
  3005.             \$('#submitSection').removeClass('active');
  3006.             \$('#purchaseSection').hide();
  3007.         }
  3008.     }
  3009.     
  3010.     // 送信
  3011.     \$('#submitBtn').on('click', function() {
  3012.         if (selectedParts.length === 0) {
  3013.             alert('パーツを選択してください。');
  3014.             return;
  3015.         }
  3016.         
  3017.         const form = \$('#estimateForm')[0];
  3018.         if (!form.checkValidity()) {
  3019.             form.reportValidity();
  3020.             return;
  3021.         }
  3022.         
  3023.         const formData = new FormData(form);
  3024.         formData.append('items', JSON.stringify(selectedParts));
  3025.         
  3026.         \$('#submitBtn').prop('disabled', true);
  3027.         \$('#loading').addClass('active');
  3028.         
  3029.         \$.ajax({
  3030.             url: '{{ url('nz_estimate_simulator_send') }}',
  3031.             type: 'POST',
  3032.             data: formData,
  3033.             processData: false,
  3034.             contentType: false,
  3035.             headers: {
  3036.                 'x-csrf-token': \$('meta[name=\"x-csrf-token\"]').attr('content')
  3037.             },
  3038.             success: function(response) {
  3039.                 \$('#loading').removeClass('active');
  3040.                 
  3041.                 if (response.success) {
  3042.                     \$('.category-section, .estimate-summary, .customer-form, .submit-section').hide();
  3043.                     \$('#completeMessage').addClass('active');
  3044.                 } else {
  3045.                     alert(response.message || '送信に失敗しました。');
  3046.                     \$('#submitBtn').prop('disabled', false);
  3047.                 }
  3048.             },
  3049.             error: function(xhr, status, error) {
  3050.                 \$('#loading').removeClass('active');
  3051.                 
  3052.                 let errorMessage = '送信中にエラーが発生しました。';
  3053.                 
  3054.                 if (xhr.responseJSON && xhr.responseJSON.message) {
  3055.                     errorMessage = xhr.responseJSON.message;
  3056.                 } else if (xhr.status === 0) {
  3057.                     errorMessage = 'ネットワークエラーが発生しました。インターネット接続を確認してください。';
  3058.                 } else if (xhr.status === 500) {
  3059.                     errorMessage = 'サーバーエラーが発生しました。しばらく時間をおいて再度お試しください。';
  3060.                 } else if (xhr.status === 403) {
  3061.                     errorMessage = 'セキュリティエラーが発生しました。ページを再読み込みして再度お試しください。';
  3062.                 }
  3063.                 
  3064.                 alert(errorMessage);
  3065.                 console.error('Error details:', {
  3066.                     status: xhr.status,
  3067.                     statusText: xhr.statusText,
  3068.                     responseText: xhr.responseText,
  3069.                     error: error
  3070.                 });
  3071.                 
  3072.                 \$('#submitBtn').prop('disabled', false);
  3073.             }
  3074.         });
  3075.     });
  3076.     // 今すぐ購入ボタン
  3077.     \$('#purchaseBtn').on('click', function(e) {
  3078.         e.preventDefault();
  3079.         
  3080.         console.log('購入ボタンがクリックされました');
  3081.         
  3082.         if (selectedParts.length === 0) {
  3083.             alert('パーツが選択されていません');
  3084.             return;
  3085.         }
  3086.         // パーツ合計を計算
  3087.         const partsTotal = Math.floor(selectedParts.reduce((sum, item) => sum + item.price, 0));
  3088.         
  3089.         // 内部料金調整を適用: 合計 = パーツ合計 × (1 - 割引率/100) + 追加料金
  3090.         let finalTotal = partsTotal * (1 - discountRate / 100) + additionalCharge;
  3091.         finalTotal = Math.floor(finalTotal); // 小数点以下切り捨て
  3092.         console.log('カート追加:', {
  3093.             partsTotal: partsTotal,
  3094.             discountRate: discountRate,
  3095.             additionalCharge: additionalCharge,
  3096.             finalTotal: finalTotal,
  3097.             selectedParts: selectedParts
  3098.         });
  3099.         // ボタンを無効化
  3100.         \$(this).prop('disabled', true).text('商品作成中...');
  3101.         // カートに追加リクエスト
  3102.         const ajaxUrl = '/estimate_simulator/add_to_cart';
  3103.         console.log('AJAX URL:', ajaxUrl);
  3104.         
  3105.         \$.ajax({
  3106.             url: ajaxUrl,
  3107.             type: 'POST',
  3108.             contentType: 'application/json',
  3109.             data: JSON.stringify({
  3110.                 selected_parts: selectedParts,
  3111.                 total_price: finalTotal
  3112.             }),
  3113.             beforeSend: function() {
  3114.                 console.log('AJAXリクエスト送信開始');
  3115.             },
  3116.             success: function(response) {
  3117.                 console.log('AJAX成功:', response);
  3118.                 if (response.success) {
  3119.                     console.log('リダイレクト開始:', response.product_url);
  3120.                     
  3121.                     // 複数の方法でリダイレクトを試行
  3122.                     try {
  3123.                         window.location.href = response.product_url;
  3124.                     } catch(e) {
  3125.                         console.error('location.href失敗:', e);
  3126.                         try {
  3127.                             window.location.assign(response.product_url);
  3128.                         } catch(e2) {
  3129.                             console.error('location.assign失敗:', e2);
  3130.                             window.location.replace(response.product_url);
  3131.                         }
  3132.                     }
  3133.                 } else {
  3134.                     alert(response.message || '商品作成に失敗しました');
  3135.                     \$('#purchaseBtn').prop('disabled', false).text('🛒 すぐに購入');
  3136.                 }
  3137.             },
  3138.             error: function(xhr, status, error) {
  3139.                 console.log('AJAXエラー:', {status: xhr.status, error: error, responseText: xhr.responseText});
  3140.                 let errorMessage = '商品作成中にエラーが発生しました';
  3141.                 if (xhr.responseJSON && xhr.responseJSON.message) {
  3142.                     errorMessage = xhr.responseJSON.message;
  3143.                 }
  3144.                 alert(errorMessage);
  3145.                 \$('#purchaseBtn').prop('disabled', false).text('🛒 すぐに購入');
  3146.             }
  3147.         });
  3148.     });
  3149.     
  3150.     // スマホ用:スクロール検知で固定サマリーを表示/非表示
  3151.     function initStickySummary() {
  3152.         if (window.innerWidth > 768) {
  3153.             return; // タブレット以上は無効
  3154.         }
  3155.         
  3156.         const \$originalSummary = \$('.estimate-summary').first();
  3157.         const \$stickySummary = \$('#stickySummary');
  3158.         
  3159.         if (\$originalSummary.length === 0 || \$stickySummary.length === 0) {
  3160.             return;
  3161.         }
  3162.         
  3163.         function checkSummaryPosition() {
  3164.             const summaryTop = \$originalSummary.offset().top;
  3165.             const summaryBottom = summaryTop + \$originalSummary.outerHeight();
  3166.             const scrollTop = \$(window).scrollTop();
  3167.             const windowBottom = scrollTop + \$(window).height();
  3168.             
  3169.             // 元のサマリーが画面外に出たら固定サマリーを表示
  3170.             if (summaryBottom < scrollTop || summaryTop > windowBottom) {
  3171.                 \$stickySummary.addClass('visible');
  3172.                 \$('body').addClass('sticky-summary-active');
  3173.             } else {
  3174.                 \$stickySummary.removeClass('visible');
  3175.                 \$('body').removeClass('sticky-summary-active');
  3176.             }
  3177.         }
  3178.         
  3179.         \$(window).on('scroll', checkSummaryPosition);
  3180.         \$(window).on('resize', checkSummaryPosition);
  3181.         
  3182.         // 初回チェック(少し遅延させる)
  3183.         setTimeout(checkSummaryPosition, 500);
  3184.     }
  3185.     
  3186.     // DOMが完全に読み込まれた後に初期化
  3187.     initStickySummary();
  3188. });
  3189. </script>
  3190. {% endblock %}
  3191. ""@NZEstimateSystem42/default/simulator.twig""/home/noie373zam/public_html/noiezam-ec/app/Plugin/NZEstimateSystem42/Resource/template/default/simulator.twig");
  3192.     }
  3193. }