From e0791e0c90408997a6cc67ecfbab31495be016ce Mon Sep 17 00:00:00 2001
From: gtbu <manbu@mail.de>
Date: Thu, 10 Apr 2025 19:56:26 +0200
Subject: [PATCH] update combine

Better path-resolution
---
 favicon.ico                                   | Bin 17542 -> 15406 bytes
 include/tool/Output/Combine.php               |  20 +-
 include/tool/Output/CombineCss.php            | 442 +++++++++++-------
 themes/Bootstrap5.3_mult/1-white/style.scss   |   2 -
 themes/Bootstrap5.3_mult/2-white/style.scss   |   1 -
 themes/Bootstrap5.3_mult/3-blue/style.scss    |   2 -
 themes/Bootstrap5.3_mult/4-green/style.scss   |   2 -
 .../_common/common_style.scss                 |   7 -
 .../_common/compensate_fixed_navbar.scss      |   4 +-
 themes/Bootstrap5.3_mult/template.php         |   9 +-
 10 files changed, 303 insertions(+), 186 deletions(-)

diff --git a/favicon.ico b/favicon.ico
index e66ba939c8fbc79262c9aaaa0ed0d51e7b24b5d8..c9f102244eed332e2616cfb2b8446860dcfc859a 100644
GIT binary patch
literal 15406
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
LqOV$D#x3w4<7&J2

literal 17542
zcmds8Yj9Op6<!otY}Mg40y0yGRnQrKi2ms4FR9M>r=!I(rFA-5)YhplY;n}8)ArU{
zAvd`sKm`<OtEf}xbR3yN$n)MLyuu?bHH3EvA)%Bu?@$5>*?!;2+2P!C&tu=4dlT`@
z?7Pp|XYaMXwf5R;uf6xVA`@hiEL<qa^W@d}B3~1cygd7Psz?XQX3rjeUUj?3`nyG@
zqMY>~pCmGOk;rGz1|(z|63cl-&@LBO&06_bRe>z6DwNgnLfMJ@IMVw_SM9|6<JP+Z
ziB-nr0k+8n7E_wFD`grm{Rn9v(h!nLGYvtWy^!UHq-Dx?wDl^AWmddE3V^jYO}s(!
zkyf;{KxU2>pI`fUL?%IBKLelcEaG;{)K^&`k!=yV%@0Q=<&{zSB<%ZO4sj>R0X-e4
zD3ZG}!Rl@UeS96byT%H;gHIQ9cCQ;xI?q)x`3~p}IA|v6X$;B-p})t{!K~|IjD8>Z
zGVv?fmT?F&d{^T#$!(h%PrbW!Fkos+)>s`Mdv{}YKX-<W=|g{#TVeBuvt`#c>!zwR
zwZB$fZF|(!z6)0BYOPhL5<gefYd+$}7y3-UL_h9lL+GPlWDEP7TklcVKDeZu*Fb-#
zx<1%%74`SFszd+yVkl-e9?n5t_-l&f<EWdJIrYHbKCcF^T^@mczJ8tBvvH1vHBo%K
zYAji%`g_|HWpv{+c<yEl;FbaHRvWZ6;zxVYS99G;&o+so+dRt+G2}b(?^-R>(XP+1
zZ6M$MFMV1=R|8i%6!o1Ax-J?$&X^DW@+YqM8S^~4<KYqf9nIw#(+R^<StyU|K0w|5
zVf9VlpR8Iwg1_y^#+>2DINnSAj9Gy#YxC@>_!A@eTi^U+&hQg6*FcC99x~OfNxd<|
zuegqb9Tl74GOaU~_<@N&bC)SLlX_!{zd&{X(<u{Nrgg>=KWyhDaJ8FaGpRS0_<`p=
zB<^u!%}<Om{{H_1{5QM(oW5D@#~!~owg0@?=P%&@*3D`^ST8OeP5gGI<|%cqZdFq1
zI9D+ezxBRbpY+aM_u!T<jK*&d>2(;_#hLWMmF{yRSTnh4EO}%!`1k4fKhyOK*H*hX
z%vOj0`6bn~=NarTyptrx#l|ga-=@2KQ}x=9W{jWv?izo#){nc_&s5wW(N_-R|6b?%
zk#fL(va)`s47wk3h(D@zuk*5enFATJvW9Qk{j}8uIl|u)j|xuDQZL~DWhUd;-R98W
z@6ILuCsUCRmvH|O^Fh5EPrA<>)4Xz^|3g9hhm<0|T0RB5qdFs^U&;ago{E?(@YB7s
z9P`?JkYO-gy*umIyf_Vew0C6eADy&tKfpbGC&sXQ%xRCJUDozhTzJxvYlvqlVR*gX
zLxx8v`zUy)gVU=nZQuu9*i6f8TN>4P$WZ?7bZy2@rs}B7&xT(zmIG%f^do+FGAVbQ
zU-uZ}NOxZ#CYPb}te;;y<+bN&#u{0ft8HEM!A@g5LvixT<vn9QJ8rGZus`VA_QzxL
zW75hc)-c-kHysbzYf-MJPat(5ac_x<_bl55yhTV4>a|B0?XdSsMJ6aEq6S62%)Cov
z3iE`>MCK8EKg67b@H52xSrJr61Cf5_^F+`L4Mm!nPri{S_&iZW=Mg?nz_&=QoR4F9
zMaawpM}p-Jyv=-e$fM=da%(+ky+B2lo<{VRu=S|*s`YH^o!@EYAuU20`A%yp9x}Xe
z{yiVQ?>^+~VJ|gE7m>Kn$8@nODm4`avYvG+3MFe_gdsMX7tfbs*l-iZrPo}TT!uQ}
z-^4a$1u{27ait<3=Rfe7kWGPiG7p1i-KHWc`M6K(`zN%S30>Afr)l#gUUz~%>d<dS
z-^GFFBD6p6rZL)Q$kc}ZyC<kWAoEP{Z#CwHzJ^>1ePt!yqt8<0*Ytp))lUz=HWTlS
zeLiim><^;$;as?W+gDZn)~~4jn-{3EmB18)0doWTBc(6E`+1EsEGK=+e5Up{>RNxd
zwI<=u;NVqt25a1S?AAaGpdWGe1N?qo7*FT>vKK_P9@%8^ZEvnrwHs!sl4nKj`SV=W
zav-MSMU#X11Alq!#@H+`l(kO!LHCyIXs@q$@g0^f*oOUI9-@D%9_qqB6j2ADw*b07
z8^qK3j(X?!A-sCuds~%0?{9C;cJ6!nD5UviQCaMM7xYa2{Jw)%XG^tWUj)_neFq)V
zS7W?w^sVFNOTfQ=o6Z0HDENam=L_hdcD(pQcFtowYctj%^`ABQ3*{1cbN(C`{@{D%
ze}MnE>VLfL53zyK_~Tr|vA*kf^gqM<fF0*5)y`*z%lVF}9QA_cpCnsDEN9|>$h)&8
zVeMgeG?%Ha3w4(N8Nl4r*-~YdbAP+LZLhk}@QSKlJ0%<g=dhS8Hu1j|t3(~$@onq-
z!rn`7TfF&mx#P4tR{EGaQMueoC##-N4fxI}Ui_g@{+9nmOv^dIWd0!r&Mox!|JnY4
zg`u-0u1cOmIp5id^}_Ve*pTrldGo^B`4dU~65tO=n;3d8y@l^D_h#(>EAzR!x^ZC+
zd;vN!@<Y@Eb=`CEaAy3Y^4ty1!*}363py}5@1l+ycRi{;=sc4)f5><cygc{mZ47Op
zeStnG$2@@gp?#f13-Kq~1g-^)<8NRHv$G`Lh0+Gvf$_KQ6H5<K{8zxx9$b99Z0JCk
z+?uz}G0)BhUTghjK;Mg}RR+HKkR#>0R?R0Y=h&ow0G+c&IM6SscecDh)-bLPqi^~i
zvF!rH%-@7gHbNf8OgeU8{J{HKtbI0UpK3~1B_@t+(_rsm9=8*ku``jt_5xD`+XqaN
z=B3*uY&y-LN!sKwvQ+`z+^67RT!^IiDVE{CXX#w<rPHD7C!r($N2~>jI$>%-jJcEV
za5k6rUL15i1-jk}I)P(NkHhk)yy~~Ubjt&Kx*zm??Up=!(hx8$cGGh{gYKxm8wS4R
zPMZ4O!tIaE!ps=C^!JEWFi$b(vzIJxIY67=YkSt!<&1B*{<(^8uz6kU?@@2Pey=YN
zw4182<J{&oy)UEbIdkHyd*yQ$-E#*D)K1LpiQ*5dqi;OyO&?<&<83SHBaWKkq~&$b
zIO^S#uUqtYV!ceeVe-?Dra}9LZLQGHemUd6&epw%i%wX2_p9eDCx4C)*i4DD-0Pn7
zyV`cE%bll<>5~rnnd35aPbp*iQESX|KReKNGNli?^d-5bpHuyy4}ANV@f64Dj;CQi
z=M$>0{e*STGA71*fx3;#4nO-O?PDe1v;K2)x&62r!2ef<t`Dj!T}`ULr^WiF<U+$=
z19<@Z3g7Vs`(-Rv6}v?>Y=6)iTYc|0+Hv6|lr!Dp>%Z+-+?pHUe_r#H(;la^pN8#A
z)s>#}Rt&DkFLTd!w8!YMf1r=R+%O%o_|~=PfY!j(9`o@PP5zHCa4hk~LS3OhxJSs`
zx1Nm=`dC8T?h7*g)VHn8!}q<G-)DZ&yuf2gkgmMwYu}D`_iH*qIr?MDNS_Dwq5Y2Y
z2kNKn-3IaCyRt%=f$z#z*glr=34IAu3u3ks%poffFU|;}Glavjek|F#a2VE%gQElv
zlOl+im`pESpKVElv`CY*8HaJA%tO*~*rG{z2)Q8Bosgp#c_VTick!NOcLp`b`%S19
zXou*_&0aLn_K>?R=TR%>K-}S+Q_*LKoiy<L7q7PQ*ksjywp?*vW%q_T$utUO6KMd?
zTz6ZJ+mgueo;2f!QuvAeFMl?vz10T#0PmFlwGeCdx@ng0dgHf&F#&aY%+23w|Kg01
zxWfHjKLb1aC1oBPZa>|mz2`ibPJ3WT!OuM{#OW6Q?Z3jDgZ}gMPp~nJ*)>`o=A54{
zo%}cElmV;#I}KY@dsC@8@~>~C&;ajZ%u^}-XW0y5v+IKcYWq_G{PTzH8FV%~G4P%;
zw6|1R*xm2=Oq+AsV2b@he#+u`e&w=XO$RiR?b!BHPX569lCJNSQy-|a#<nZ%myS_a
bNGa#1T|A4&xpe|(*%1TR!#b?XHf;M}yh72o

diff --git a/include/tool/Output/Combine.php b/include/tool/Output/Combine.php
index 1d9aa6a..0b1d097 100644
--- a/include/tool/Output/Combine.php
+++ b/include/tool/Output/Combine.php
@@ -669,7 +669,7 @@ class Combine{
 	/**
 	 * Combine CSS files
 	 */
-	public function CombineCSS($full_paths){
+	public function CombineCSSold($full_paths){
 
 		$imports			= '';
 		$combined_content	= '';
@@ -699,6 +699,24 @@ class Combine{
 	}
 
 
+    public function CombineCSS($full_paths){ // Renamed for clarity, use your actual function name
+
+    $all_prepended_imports = ''; 
+    $combined_content    = '';
+    // $new_imported        = []; // Caching logic needs rethinking based on new class
+
+    foreach($full_paths as $file => $full_path){
+        $temp = new \gp\tool\Output\CombineCss($file);
+        $combined_content .= "\n/* " . htmlspecialchars($file) . " */\n";
+        $combined_content .= $temp->final_content; //  final_content instead of content
+        $all_prepended_imports .= $temp->prepended_imports; // prepended_imports instead of imports
+    }
+
+    $combined_content = $all_prepended_imports . $combined_content;
+	
+    return $combined_content;
+    }
+
 	/**
 	 * Combine JS files
 	 *
diff --git a/include/tool/Output/CombineCss.php b/include/tool/Output/CombineCss.php
index 2215ba6..8782753 100644
--- a/include/tool/Output/CombineCss.php
+++ b/include/tool/Output/CombineCss.php
@@ -1,209 +1,325 @@
 <?php
-
 namespace gp\tool\Output;
 
 defined('is_running') or die('Not an entry point...');
 
-/**
- * Get the contents of $file and fix paths:
- * 	- url(..)
- *	- @import
- * 	- @import url(..)
- */
-class CombineCSS{
+class CombineCSS {
 
-	public $content;
-	public $file;
-	public $full_path;
-	public $imported = array();
-	public $imports = '';
+    // Public properties to access results
+    public $combined_content_raw = ''; // The combined, path-fixed CSS before minification
+    public $final_content = '';      // The final output (minified if requested/possible)
+    public $prepended_imports = '';  // @import rules that were kept (remote, media queries)
+    public $processed_files = [];    // Files processed in this instance to prevent loops
 
-	public function __construct($file){
-		global $dataDir;
+    // Internal properties
+    private $entry_file_path;        // Normalized path relative to $dataDir for the initial file
+    private $minify_output;          // Flag whether to minify the final output
+    private $dataDir_norm;           // Normalized $dataDir path
 
-		includeFile('thirdparty/cssmin_v.1.0.php');
+    /**
+     * Constructor
+     *
+     * @param string $file Path to the entry CSS file, relative to $dataDir
+     * @param bool $minify Whether to minify the final output
+     */
+    public function __construct($file, $minify = true) {
+        global $dataDir;
+        $this->minify_output = $minify;
 
-		$this->file = $file;
-		$this->full_path = $dataDir.$file;
+        // --- Path Normalization ---
+        // Ensure $dataDir ends with a slash and uses forward slashes
+        $this->dataDir_norm = rtrim(str_replace('\\', '/', $dataDir), '/') . '/';
+        // Ensure $file starts without a slash and uses forward slashes
+        $this->entry_file_path = ltrim(str_replace('\\', '/', $file), '/');
 
+        $full_path = $this->dataDir_norm . $this->entry_file_path;
+        $real_full_path = realpath($full_path);
 
-		$this->content = file_get_contents($this->full_path);
-		$this->content = \cssmin::minify($this->content);
+        if (!$real_full_path || !file_exists($real_full_path)) {
+            trigger_error('CombineCSS: Entry file not found: ' . htmlspecialchars($full_path), E_USER_WARNING);
+            $this->final_content = '/* CombineCSS Error: Entry file not found: ' . htmlspecialchars($file) . ' */';
+            return;
+        }
 
-		$this->CSS_Import();
-		$this->CSS_FixUrls();
-	}
+        // --- Load Minifier if needed ---
+        if ($this->minify_output) {
+            // Consider adding a check if the class/function exists after include
+            if (!class_exists('\cssmin')) {
+                 includeFile('thirdparty/cssmin_v.1.0.php');
+                 if (!class_exists('\cssmin')) {
+                      trigger_error('CombineCSS: cssmin class not found after including thirdparty/cssmin_v.1.0.php. Minification disabled.', E_USER_WARNING);
+                      $this->minify_output = false; // Disable minification if class isn't there
+                 }
+            }
+        }
 
+        // --- Start Processing ---
+        $this->processed_files = []; // Reset for this instance
+        $this->prepended_imports = '';
+        $this->combined_content_raw = $this->processFile($real_full_path);
 
-	/**
-	 * Include the css from @imported css
-	 *
-	 * Will include the css from these
-	 * @import "../styles.css";
-	 * @import url("../styles.css");
-	 * @import styles.css;
-	 *
-	 *
-	 * Will preserve the @import rule for these
-	 * @import "styles.css" screen,tv;
-	 * @import url('http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/themes/smoothness/jquery-ui.css.css');
-	 *
-	 */
-	public function CSS_Import($offset=0){
-		global $dataDir;
+        // Prepend any imports that were kept
+        $this->combined_content_raw = $this->prepended_imports . $this->combined_content_raw;
 
-		$pos = strpos($this->content,'@import ',$offset);
-		if( !is_numeric($pos) ){
-			return;
-		}
-		$replace_start = $pos;
-		$pos += 8;
+        // --- Final Minification ---
+        if ($this->minify_output) {
+            try {
+                // Ensure cssmin class exists before calling
+                 if (class_exists('\cssmin')) {
+                     $this->final_content = \cssmin::minify($this->combined_content_raw);
+                 } else {
+                      // Should have been caught earlier, but as a fallback
+                      $this->final_content = $this->combined_content_raw;
+                 }
+            } catch (\Exception $e) {
+                trigger_error('CombineCSS: Minification failed for ' . htmlspecialchars($file) . ': ' . $e->getMessage(), E_USER_WARNING);
+                $this->final_content = $this->combined_content_raw; // Fallback to raw content on error
+            }
+        } else {
+            $this->final_content = $this->combined_content_raw;
+        }
+    }
 
-		$replace_end = strpos($this->content,';',$pos);
-		if( !is_numeric($replace_end) ){
-			return;
-		}
+    /**
+     * Process a single CSS file: read, handle imports, fix URLs.
+     * Returns the processed (but not minified) CSS content.
+     *
+     * @param string $real_path Absolute filesystem path to the CSS file.
+     * @return string Processed CSS content.
+     */
+    private function processFile($real_path) {
+        // --- Prevent Infinite Loops ---
+        if (isset($this->processed_files[$real_path])) {
+            trigger_error('CombineCSS: Recursive import detected and skipped for: ' . htmlspecialchars($real_path), E_USER_WARNING);
+            return '/* CombineCSS Error: Recursive import skipped: ' . htmlspecialchars(basename($real_path)) . ' */';
+        }
+        $this->processed_files[$real_path] = true;
 
-		$import_orig = substr($this->content,$pos,$replace_end-$pos);
-		$import_orig = trim($import_orig);
-		$replace_len = $replace_end-$replace_start+1;
+        // --- Read File ---
+        $content = @file_get_contents($real_path); // Use @ to suppress warning, check result
+        if ($content === false) {
+            trigger_error('CombineCSS: Could not read file: ' . htmlspecialchars($real_path), E_USER_WARNING);
+            unset($this->processed_files[$real_path]); // Allow reprocessing if attempted again? Maybe not.
+            return '/* CombineCSS Error: Could not read file: ' . htmlspecialchars(basename($real_path)) . ' */';
+        }
 
-		//get url(..)
-		$media = '';
-		if( substr($import_orig,0,4) == 'url(' ){
-			$end_url_pos = strpos($import_orig,')');
-			$import = substr($import_orig,4, $end_url_pos-4);
-			$import = trim($import);
-			$import = trim($import,'"\'');
-			$media = substr($import_orig,$end_url_pos+1);
-		}elseif( $import_orig[0] == '"' || $import_orig[0] == "'" ){
-			$end_url_pos = strpos($import_orig,$import_orig[0],1);
-			$import = substr($import_orig,1, $end_url_pos-1);
-			$import = trim($import);
-			$media = substr($import_orig,$end_url_pos+1);
-		}
+        $source_dir = dirname($real_path); // Directory of the current file
+        $output_buffer = '';
+        $offset = 0;
 
+        // --- Process @import Rules ---
+        // Regex to find @import rules, handling url() and quoted strings
+        $import_pattern = '/@import\s+(?:url\(\s*([\'"]?)(.*?)\1\s*\)|([\'"])(.*?)\3)\s*([^;]*);/i';
 
-		// keep @import when the file is on a remote server?
-		if( strpos($import,'//') !== false ){
-			$this->imports .= substr($this->content, $replace_start, $replace_len );
-			$this->content = substr_replace( $this->content, '', $replace_start, $replace_len);
-			$this->CSS_Import($offset);
-			return;
-		}
+        while (preg_match($import_pattern, $content, $matches, PREG_OFFSET_CAPTURE, $offset)) {
+            $match_info = $matches[0]; // [full_match_string, offset]
+            $full_match = $match_info[0];
+            $match_start = $match_info[1];
 
+            // Append the content *before* this @import rule, fixing its URLs
+            $content_before = substr($content, $offset, $match_start - $offset);
+            $output_buffer .= $this->fixUrlsInBlock($content_before, $source_dir);
 
-		//if a media type is set, keep the @import
-		$media = trim($media);
-		if( !empty($media) ){
-			$import = \gp\tool::GetDir(dirname($this->file).'/'.$import);
-			$import = $this->ReduceUrl($import);
-			$this->imports .= '@import url("'.$import.'") '.$media.';';
-			$this->content = substr_replace( $this->content, '', $replace_start, $replace_len);
-			$this->CSS_Import($offset);
-			return;
-		}
+            // Extract import details
+            // Group 2 is URL from url('...'), Group 4 is URL from "..."
+            $import_url = trim(!empty($matches[2][0]) ? $matches[2][0] : $matches[4][0]);
+            $media_query = trim($matches[5][0]);
 
+            $is_remote = preg_match('#^(https?:|//)#i', $import_url);
+            $has_media = !empty($media_query);
 
-		//include the css
-		$full_path = false;
-		if( $import[0] != '/' ){
-			$import = dirname($this->file).'/'.$import;
-			$import = $this->ReduceUrl($import);
-		}
-		$full_path = $dataDir.$import;
+            if ($is_remote || $has_media) {
+                // --- Keep Import Rule ---
+                $resolved_import_url = $import_url;
+                if (!$is_remote && !$this->isAbsoluteUrl($import_url)) {
+                    // Resolve local path relative to *this* file's directory
+                    $resolved_import_url = $this->resolvePath($import_url, $source_dir);
+                }
+                // Add to the list of imports to prepend later
+                $this->prepended_imports .= '@import url("' . $resolved_import_url . '")' . ($has_media ? ' ' . $media_query : '') . ";\n";
 
-		if( file_exists($full_path) ){
+            } else {
+                // --- Inline Import Rule ---
+                if ($this->isAbsoluteUrl($import_url)) {
+                     // Handle root-relative imports (e.g., /css/other.css)
+                     $import_full_path = $this->dataDir_norm . ltrim($import_url, '/');
+                } else {
+                     // Handle relative imports (e.g., ../common.css)
+                     $import_full_path = $source_dir . '/' . $import_url;
+                }
 
-			$temp = new \gp\tool\Output\CombineCss($import);
-			$this->content = substr_replace($this->content,$temp->content,$replace_start,$replace_end-$replace_start+1);
-			$this->imported[] = $full_path;
-			$this->imported = array_merge($this->imported,$temp->imported);
-			$this->imports .= $temp->imports;
+                // Canonicalize and get real path
+                $import_real_path = realpath($this->normalizePath($import_full_path));
 
-			$this->CSS_Import($offset);
-			return;
-		}
+                if ($import_real_path && file_exists($import_real_path)) {
+                    // Recursively process the imported file
+                    $imported_content = $this->processFile($import_real_path);
+                    $output_buffer .= $imported_content; // Append processed content
+                } else {
+                    trigger_error('CombineCSS: Imported file not found or path error: ' . htmlspecialchars($import_full_path) . ' (referenced in ' . htmlspecialchars($real_path) . ')', E_USER_WARNING);
+                    $output_buffer .= '/* CombineCSS Error: Import not found: ' . htmlspecialchars($import_url) . ' */';
+                }
+            }
 
-		$this->CSS_Import($pos);
-	}
+            // Move offset past the processed @import rule
+            $offset = $match_start + strlen($full_match);
 
+        } // End while loop for @import
 
-	public function CSS_FixUrls($offset=0){
-		$pos = strpos($this->content,'url(',$offset);
-		if( !is_numeric($pos) ){
-			return;
-		}
-		$pos += 4;
+        // --- Process Remaining Content ---
+        // Append the rest of the file content (after the last @import), fixing its URLs
+        $content_after = substr($content, $offset);
+        $output_buffer .= $this->fixUrlsInBlock($content_after, $source_dir);
 
-		$pos2 = strpos($this->content,')',$pos);
-		if( !is_numeric($pos2) ){
-			return;
-		}
-		$url = substr($this->content,$pos,$pos2-$pos);
+        // Clean up recursion tracking for this file (allows it to be imported again via a different path if needed, though generally discouraged)
+        // unset($this->processed_files[$real_path]); // Optional: Decide if a file should *never* be processed twice per instance
 
-		$this->CSS_FixUrl($url,$pos,$pos2);
+        return $output_buffer;
+    }
 
-		return $this->CSS_FixUrls($pos2);
-	}
+    /**
+     * Finds all url() references in a block of CSS and fixes relative paths.
+     *
+     * @param string $content_block CSS content segment.
+     * @param string $source_dir Absolute directory path of the file this content came from.
+     * @return string CSS content segment with URLs fixed.
+     */
+    private function fixUrlsInBlock($content_block, $source_dir) {
+        if (empty($content_block) || strpos($content_block, 'url(') === false) {
+            return $content_block;
+        }
 
-	public function CSS_FixUrl($url,$pos,$pos2){
-		global $dataDir;
+        // Regex to find url(...) patterns, handling optional quotes
+        $url_pattern = '/url\(\s*([\'"]?)(.*?)\1\s*\)/i';
 
-		$url = trim($url);
-		$url = trim($url,'"\'');
+        return preg_replace_callback(
+            $url_pattern,
+            function ($matches) use ($source_dir) {
+                $original_match = $matches[0]; // The full url(...) match
+                $url = trim($matches[2]);     // The actual URL inside
 
-		if( empty($url) ){
-			return;
-		}
+                // Don't modify absolute URLs, data URIs, or empty URLs
+                if (empty($url) || $this->isAbsoluteUrl($url) || strncasecmp($url, 'data:', 5) === 0) {
+                    return $original_match; // Return the original match unchanged
+                }
 
-		//relative url
-		if( $url[0] == '/' ){
-			return;
-		}elseif( strpos($url,'://') > 0 ){
-			return;
-		}elseif( preg_match('/^data:/i', $url) ){
-			return;
-		}
+                // Resolve the relative path
+                $resolved_url = $this->resolvePath($url, $source_dir);
 
+                // Return the corrected url() string, always quoted for safety
+                return 'url("' . $resolved_url . '")';
+            },
+            $content_block
+        );
+    }
 
-		//use a relative path so sub.domain.com and domain.com/sub both work
-		$replacement = \gp\tool::GetDir(dirname($this->file).'/'.$url);
-		$replacement = $this->ReduceUrl($replacement);
+    /**
+     * Resolves a relative URL from a CSS file to be relative to the web root ($dataDir).
+     *
+     * @param string $relative_url The relative URL found in the CSS.
+     * @param string $source_dir The absolute directory path where the CSS file resides.
+     * @return string The resolved URL, typically starting with '/' relative to the web root.
+     */
+    private function resolvePath($relative_url, $source_dir) {
+    // Separate query string and fragment
+    $query_fragment = '';
+    $path_only = $relative_url;
+    if (($pos = strpos($relative_url, '?')) !== false || ($pos = strpos($relative_url, '#')) !== false) {
+         $query_fragment = substr($relative_url, $pos);
+         $path_only = substr($relative_url, 0, $pos);
+    }
 
+    $combined_path = $source_dir . '/' . $path_only;
+    $reduced_path = $this->reducePath($this->normalizePath($combined_path));
 
-		$replacement = '"'.$replacement.'"';
-		$this->content = substr_replace($this->content,$replacement,$pos,$pos2-$pos);
-	}
+    error_log("--- resolvePath Check ---");
+    error_log("Relative URL: " . $relative_url);
+    error_log("Source Dir: " . $source_dir);
+    error_log("Reduced FS Path: " . $reduced_path);
+    error_log("DataDir Norm: " . $this->dataDir_norm);
 
-	/**
-	 * Canonicalize a path by resolving references to '/./', '/../'
-	 * Does not remove leading "../"
-	 * @param string path or url
-	 * @return string Canonicalized path
-	 *
-	 */
-	public function ReduceUrl($url){
+    $starts_with_check = (stripos($reduced_path, $this->dataDir_norm) === 0);
+    error_log("Does Reduced start w/ DataDir (Case-Insensitive)? " . ($starts_with_check ? 'YES' : 'NO'));
 
-		$temp = explode('/',$url);
-		$result = array();
-		foreach($temp as $i => $path){
-			if( $path == '.' ){
-				continue;
-			}
-			if( $path == '..' ){
-				for($j=$i-1;$j>0;$j--){
-					if( isset($result[$j]) ){
-						unset($result[$j]);
-						continue 2;
-					}
-				}
-			}
-			$result[$i] = $path;
-		}
-
-		return implode('/',$result);
-	}
+    if ($starts_with_check) {
+        // Get path part after dataDir
+        $path_relative_to_datadir = substr($reduced_path, strlen($this->dataDir_norm));
 
+        // --- Use Typesetter's Base URL ---
+        // Assumes \gp\tool::GetUrl('') returns http://localhost/T53test5g5/
+        // Or use \gp\tool::GetDir('') if that returns /T53test5g5/
+        $base_url = \gp\tool::GetDir(''); // Get base path like /T53test5g5/
+        $final_web_path = rtrim($base_url, '/') . '/' . ltrim($path_relative_to_datadir, '/');
+        // --- End Modification ---
 
+        error_log("Resolved Web Path (Using Base Path): " . $final_web_path . $query_fragment);
+        return $final_web_path . $query_fragment;
+    } else {
+        // ... (existing trigger_error code) ...
+        $fallback_path = $path_only;
+        error_log("Resolved Fallback Path (Outside dataDir): " . $fallback_path . $query_fragment);
+        return $fallback_path . $query_fragment;
+    }
 }
+
+    /**
+     * Normalizes a path: forward slashes, no duplicate slashes.
+     *
+     * @param string $path
+     * @return string
+     */
+    private function normalizePath($path) {
+        return preg_replace('#/+#', '/', str_replace('\\', '/', $path));
+    }
+
+    /**
+     * Canonicalizes a path by resolving '/./' and '/../'.
+     *
+     * @param string $path Normalized path.
+     * @return string Canonicalized path.
+     */
+    private function reducePath($path) {
+        $parts = explode('/', $path);
+        $result = [];
+        $is_absolute = str_starts_with($path, '/');
+
+        foreach ($parts as $part) {
+            if ($part === '.' || $part === '') {
+                continue;
+            }
+            if ($part === '..') {
+                // Only pop if result is not empty and the last element is not '..'
+                // This prevents going above the root in relative paths like ../../file
+                 if (!empty($result) && end($result) !== '..') {
+                    array_pop($result);
+                } elseif (!$is_absolute) {
+                    // Keep '..' if path is relative and we are at the start or after other '..'
+                     $result[] = '..';
+                }
+                 // If absolute, popping '..' at the root does nothing
+            } else {
+                $result[] = $part;
+            }
+        }
+
+        // Handle the case of an absolute path resolving to root ('/')
+        $final_path = implode('/', $result);
+        if ($is_absolute) {
+             return '/' . $final_path;
+        } else {
+             // Handle empty result for relative path (e.g. "dir/..") -> "."
+             return ($final_path === '') ? '.' : $final_path;
+        }
+    }
+
+    /**
+     * Checks if a URL is absolute (protocol-relative, http, https, data, or root-relative).
+     *
+     * @param string $url
+     * @return bool
+     */
+    private function isAbsoluteUrl($url) {
+        // Scheme relative (//), http/https, data URI, or root relative (/)
+        return preg_match('#^(\/\/|https?:|data:|/)#i', $url);
+    }
+}
\ No newline at end of file
diff --git a/themes/Bootstrap5.3_mult/1-white/style.scss b/themes/Bootstrap5.3_mult/1-white/style.scss
index aa7ac4b..a6e801c 100644
--- a/themes/Bootstrap5.3_mult/1-white/style.scss
+++ b/themes/Bootstrap5.3_mult/1-white/style.scss
@@ -2,8 +2,6 @@
 
 @import 'include/thirdparty/Bootstrap5.3/scss/_bootstrap.scss';
 
-/*! @import '_variables.scss'; */
-
 $text-shadow: 0 1px 0 rgba(0, 0, 0, .05) !default;
 
 $navbar-height: ($nav-link-height + $navbar-padding-y * 2);
diff --git a/themes/Bootstrap5.3_mult/2-white/style.scss b/themes/Bootstrap5.3_mult/2-white/style.scss
index f37912c..d1cb10b 100644
--- a/themes/Bootstrap5.3_mult/2-white/style.scss
+++ b/themes/Bootstrap5.3_mult/2-white/style.scss
@@ -1,7 +1,6 @@
 
 @import 'include/thirdparty/Bootstrap5.3/scss/_bootstrap.scss';
 
-/*! @import '_variables.scss'; */
 
 $text-shadow: 0 1px 0 rgba(0, 0, 0, .05) !default;
 
diff --git a/themes/Bootstrap5.3_mult/3-blue/style.scss b/themes/Bootstrap5.3_mult/3-blue/style.scss
index bb2339e..2877687 100644
--- a/themes/Bootstrap5.3_mult/3-blue/style.scss
+++ b/themes/Bootstrap5.3_mult/3-blue/style.scss
@@ -1,8 +1,6 @@
 
 @import 'include/thirdparty/Bootstrap5.3/scss/_bootstrap.scss';
 
-/*! @import '_variables.scss'; */
-
 $text-shadow: 0 1px 0 rgba(0, 0, 0, .05) !default;
 
 $navbar-height: ($nav-link-height + $navbar-padding-y * 2);
diff --git a/themes/Bootstrap5.3_mult/4-green/style.scss b/themes/Bootstrap5.3_mult/4-green/style.scss
index b1a58d3..0692c88 100644
--- a/themes/Bootstrap5.3_mult/4-green/style.scss
+++ b/themes/Bootstrap5.3_mult/4-green/style.scss
@@ -1,7 +1,5 @@
 @import 'include/thirdparty/Bootstrap5.3/scss/_bootstrap.scss';
 
-/*! @import '_variables.scss'; */
-
 $text-shadow: 0 1px 0 rgba(0, 0, 0, .05) !default;
 
 $navbar-height: ($nav-link-height + $navbar-padding-y * 2);
diff --git a/themes/Bootstrap5.3_mult/_common/common_style.scss b/themes/Bootstrap5.3_mult/_common/common_style.scss
index d6872d0..820b052 100644
--- a/themes/Bootstrap5.3_mult/_common/common_style.scss
+++ b/themes/Bootstrap5.3_mult/_common/common_style.scss
@@ -1,10 +1,3 @@
-// Theme Bootswatch 4 Scss
-// Common styles used in all layouts
-//
-// Instead of Autoprefixer we use vendor-prefix mixins,
-// see /include/thirdparty/Bootstrap4/scss/bootstrap/vp-mixins
-//
-// Migrate to Bootstrap 4: check out https://getbootstrap.com/docs/4.4/migration/
 
 
 // Imports
diff --git a/themes/Bootstrap5.3_mult/_common/compensate_fixed_navbar.scss b/themes/Bootstrap5.3_mult/_common/compensate_fixed_navbar.scss
index 492685c..b535fc6 100644
--- a/themes/Bootstrap5.3_mult/_common/compensate_fixed_navbar.scss
+++ b/themes/Bootstrap5.3_mult/_common/compensate_fixed_navbar.scss
@@ -1,6 +1,4 @@
-// Theme Bootswatch 4 Scss
-// Compensations for fixed navbar
-// only required when the main navbar has the css class 'fixed-top'
+
 
 // padding-top for the body element
 body {
diff --git a/themes/Bootstrap5.3_mult/template.php b/themes/Bootstrap5.3_mult/template.php
index 60573c6..0e21ec9 100644
--- a/themes/Bootstrap5.3_mult/template.php
+++ b/themes/Bootstrap5.3_mult/template.php
@@ -1,10 +1,9 @@
   <?php
-/**
- * Theme Bootswatch 5 Scss 5.2.3
- * Typesetter CMS theme template
- * based on https://bootswatch.com
- *
+ 
+ /**
+  * Typesetter CMS theme template
  */
+ 
 global $page, $config;
 $path = $page->theme_dir . '/drop_down_menu.php';
 //include_once($path);